From 12ca1739d4266c5110605808fd15acbd60b2c126 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 23 Oct 2024 15:30:07 +0300 Subject: [PATCH 001/108] Remove all queue types except Kafka --- .../src/main/resources/thingsboard.yml | 128 +------ common/queue/pom.xml | 8 - .../queue/RuleEngineTbQueueAdminFactory.java | 56 ---- .../azure/servicebus/TbServiceBusAdmin.java | 136 -------- .../TbServiceBusConsumerTemplate.java | 175 ---------- .../TbServiceBusProducerTemplate.java | 110 ------- .../servicebus/TbServiceBusQueueConfigs.java | 72 ---- .../servicebus/TbServiceBusSettings.java | 37 --- .../provider/AwsSqsMonolithQueueFactory.java | 311 ------------------ .../provider/AwsSqsTbCoreQueueFactory.java | 291 ---------------- .../AwsSqsTbRuleEngineQueueFactory.java | 208 ------------ .../AwsSqsTbVersionControlQueueFactory.java | 98 ------ .../provider/AwsSqsTransportQueueFactory.java | 153 --------- .../provider/PubSubMonolithQueueFactory.java | 309 ----------------- .../provider/PubSubTbCoreQueueFactory.java | 278 ---------------- .../PubSubTbRuleEngineQueueFactory.java | 203 ------------ .../PubSubTbVersionControlQueueFactory.java | 98 ------ .../provider/PubSubTransportQueueFactory.java | 153 --------- .../RabbitMqMonolithQueueFactory.java | 307 ----------------- .../provider/RabbitMqTbCoreQueueFactory.java | 278 ---------------- .../RabbitMqTbRuleEngineQueueFactory.java | 202 ------------ .../RabbitMqTbVersionControlQueueFactory.java | 98 ------ .../RabbitMqTransportQueueFactory.java | 154 --------- .../ServiceBusMonolithQueueFactory.java | 306 ----------------- .../ServiceBusTbCoreQueueFactory.java | 278 ---------------- .../ServiceBusTbRuleEngineQueueFactory.java | 202 ------------ ...erviceBusTbVersionControlQueueFactory.java | 98 ------ .../ServiceBusTransportQueueFactory.java | 155 --------- .../server/queue/pubsub/TbPubSubAdmin.java | 231 ------------- .../pubsub/TbPubSubConsumerTemplate.java | 175 ---------- .../pubsub/TbPubSubProducerTemplate.java | 137 -------- .../server/queue/pubsub/TbPubSubSettings.java | 83 ----- .../pubsub/TbPubSubSubscriptionSettings.java | 72 ---- .../queue/rabbitmq/TbRabbitMqAdmin.java | 94 ------ .../rabbitmq/TbRabbitMqConsumerTemplate.java | 144 -------- .../rabbitmq/TbRabbitMqProducerTemplate.java | 125 ------- .../rabbitmq/TbRabbitMqQueueArguments.java | 111 ------- .../queue/rabbitmq/TbRabbitMqSettings.java | 66 ---- .../queue/sqs/AwsSqsTbQueueMsgMetadata.java | 28 -- .../server/queue/sqs/TbAwsSqsAdmin.java | 117 ------- .../queue/sqs/TbAwsSqsConsumerTemplate.java | 188 ----------- .../queue/sqs/TbAwsSqsProducerTemplate.java | 117 ------- .../queue/sqs/TbAwsSqsQueueAttributes.java | 105 ------ .../server/queue/sqs/TbAwsSqsSettings.java | 48 --- .../TbRabbitMqConsumerTemplateTest.java | 128 ------- .../config/custom-environment-variables.yml | 28 +- msa/js-executor/config/default.yml | 17 - msa/js-executor/queue/awsSqsTemplate.ts | 198 ----------- msa/js-executor/queue/kafkaTemplate.ts | 1 + msa/js-executor/queue/pubSubTemplate.ts | 162 --------- msa/js-executor/queue/rabbitmqTemplate.ts | 128 ------- msa/js-executor/queue/serviceBusTemplate.ts | 175 ---------- msa/js-executor/server.ts | 12 - .../src/main/resources/tb-vc-executor.yml | 91 +---- pom.xml | 16 - .../src/main/resources/tb-coap-transport.yml | 94 +----- .../src/main/resources/tb-http-transport.yml | 95 +----- .../src/main/resources/tb-lwm2m-transport.yml | 92 +----- .../src/main/resources/tb-mqtt-transport.yml | 94 +----- .../src/main/resources/tb-snmp-transport.yml | 94 +----- 60 files changed, 27 insertions(+), 8141 deletions(-) delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusAdmin.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusConsumerTemplate.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusProducerTemplate.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusQueueConfigs.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusSettings.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsMonolithQueueFactory.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbCoreQueueFactory.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbRuleEngineQueueFactory.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbVersionControlQueueFactory.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTransportQueueFactory.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbRuleEngineQueueFactory.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbVersionControlQueueFactory.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTransportQueueFactory.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqMonolithQueueFactory.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbCoreQueueFactory.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbRuleEngineQueueFactory.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbVersionControlQueueFactory.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTransportQueueFactory.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusMonolithQueueFactory.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbCoreQueueFactory.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbRuleEngineQueueFactory.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbVersionControlQueueFactory.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTransportQueueFactory.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubAdmin.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSettings.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSubscriptionSettings.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqAdmin.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqConsumerTemplate.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqProducerTemplate.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqQueueArguments.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqSettings.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/sqs/AwsSqsTbQueueMsgMetadata.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsAdmin.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsConsumerTemplate.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsProducerTemplate.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsQueueAttributes.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsSettings.java delete mode 100644 common/queue/src/test/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqConsumerTemplateTest.java delete mode 100644 msa/js-executor/queue/awsSqsTemplate.ts delete mode 100644 msa/js-executor/queue/pubSubTemplate.ts delete mode 100644 msa/js-executor/queue/rabbitmqTemplate.ts delete mode 100644 msa/js-executor/queue/serviceBusTemplate.ts diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index e9c8668958..cbfafcd01e 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1439,7 +1439,7 @@ swagger: # Queue configuration parameters queue: - type: "${TB_QUEUE_TYPE:in-memory}" # in-memory or kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) + type: "${TB_QUEUE_TYPE:in-memory}" # in-memory or kafka (Apache Kafka) prefix: "${TB_QUEUE_PREFIX:}" # Global queue prefix. If specified, prefix is added before default topic name: 'prefix.default_topic_name'. Prefix is applied to all topics (and consumer groups for kafka). in_memory: stats: @@ -1561,122 +1561,6 @@ queue: print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}" # Time to wait for the stats-loading requests to Kafka to finish kafka-response-timeout-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_RESPONSE_TIMEOUT_MS:1000}" - aws_sqs: - # Use the default credentials provider for AWS SQS - use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}" - # Access key ID from AWS IAM user - access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" - # Secret access key from AWS IAM user - secret_access_key: "${TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY:YOUR_SECRET}" - # Region from AWS account - region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}" - # Number of threads per each AWS SQS queue in consumer - threads_per_topic: "${TB_QUEUE_AWS_SQS_THREADS_PER_TOPIC:1}" - # Thread pool size for aws_sqs queue producer executor provider. Default value equals to AmazonSQSAsyncClient.DEFAULT_THREAD_POOL_SIZE - producer_thread_pool_size: "${TB_QUEUE_AWS_SQS_EXECUTOR_THREAD_POOL_SIZE:50}" - queue-properties: - # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - rule-engine: "${TB_QUEUE_AWS_SQS_RE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800 - js-executor: "${TB_QUEUE_AWS_SQS_JE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - ota-updates: "${TB_QUEUE_AWS_SQS_OTA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - version-control: "${TB_QUEUE_AWS_SQS_VC_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - edge: "${TB_QUEUE_AWS_SQS_EDGE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - pubsub: - # Project ID from Google Cloud - project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}" - # API Credentials in JSON format - service_account: "${TB_QUEUE_PUBSUB_SERVICE_ACCOUNT:YOUR_SERVICE_ACCOUNT}" - # Message size for PubSub queue.Value in bytes - max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" - # Number of messages per consumer - max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" - # Thread pool size for pubsub queue executor provider. If set to 0 - default pubsub executor provider value will be used (5 * number of available processors) - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:0}" - queue-properties: - # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again - rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - # Pub/Sub properties for Core subscribers, messages which will commit after ackDeadlineInSec period can be consumed again - core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - # Pub/Sub properties for Transport API subscribers, messages which will commit after ackDeadlineInSec period can be consumed again - transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - # Pub/Sub properties for Version Control subscribers, messages which will commit after ackDeadlineInSec period can be consumed again - notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - # PubSub queue properties - js-executor: "${TB_QUEUE_PUBSUB_JE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - # Pub/Sub properties for Transport Api subscribers, messages which will commit after ackDeadlineInSec period can be consumed again - version-control: "${TB_QUEUE_PUBSUB_VC_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - # Pub/Sub properties for Edge subscribers, messages which will commit after ackDeadlineInSec period can be consumed again - edge: "${TB_QUEUE_PUBSUB_EDGE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - service_bus: - # Azure namespace - namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}" - # Azure Service Bus Shared Access Signatures key name - sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}" - # Azure Service Bus Shared Access Signatures key - sas_key: "${TB_QUEUE_SERVICE_BUS_SAS_KEY:YOUR_SAS_KEY}" - # Number of messages per a consumer - max_messages: "${TB_QUEUE_SERVICE_BUS_MAX_MESSAGES:1000}" - queue-properties: - # Azure Service Bus properties for Rule Engine queues - rule-engine: "${TB_QUEUE_SERVICE_BUS_RE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - # Azure Service Bus properties for Core queues - core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - # Azure Service Bus properties for Transport Api queues - transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - # Azure Service Bus properties for Notification queues - notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - # Azure Service Bus queue properties - js-executor: "${TB_QUEUE_SERVICE_BUS_JE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - # Azure Service Bus properties for Version Control queues - version-control: "${TB_QUEUE_SERVICE_BUS_VC_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - # Azure Service Bus properties for Edge queues - edge: "${TB_QUEUE_SERVICE_BUS_EDGE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - rabbitmq: - # By default empty - exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}" - # RabbitMQ host used to establish connection - host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}" - # RabbitMQ host used to establish a connection - port: "${TB_QUEUE_RABBIT_MQ_PORT:5672}" - # Virtual hosts provide logical grouping and separation of resources - virtual_host: "${TB_QUEUE_RABBIT_MQ_VIRTUAL_HOST:/}" - # Username for RabbitMQ user account - username: "${TB_QUEUE_RABBIT_MQ_USERNAME:YOUR_USERNAME}" - # User password for RabbitMQ user account - password: "${TB_QUEUE_RABBIT_MQ_PASSWORD:YOUR_PASSWORD}" - # Network connection between clients and RabbitMQ nodes can fail. RabbitMQ Java client supports automatic recovery of connections and topology (queues, exchanges, bindings, and consumers) - automatic_recovery_enabled: "${TB_QUEUE_RABBIT_MQ_AUTOMATIC_RECOVERY_ENABLED:false}" - # The connection timeout for the RabbitMQ connection factory - connection_timeout: "${TB_QUEUE_RABBIT_MQ_CONNECTION_TIMEOUT:60000}" - # RabbitMQ has a timeout for connection handshake. When clients run in heavily constrained environments, it may be necessary to increase the timeout - handshake_timeout: "${TB_QUEUE_RABBIT_MQ_HANDSHAKE_TIMEOUT:10000}" - # The maximum number of messages returned in a single call of doPoll() method - max_poll_messages: "${TB_QUEUE_RABBIT_MQ_MAX_POLL_MESSAGES:1}" - queue-properties: - # RabbitMQ properties for Rule Engine queues - rule-engine: "${TB_QUEUE_RABBIT_MQ_RE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" - # RabbitMQ properties for Core queues - core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" - # RabbitMQ properties for Transport API queues - transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" - # RabbitMQ properties for Notification queues - notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" - # RabbitMQ queue properties - js-executor: "${TB_QUEUE_RABBIT_MQ_JE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" - # RabbitMQ properties for Version Control queues - version-control: "${TB_QUEUE_RABBIT_MQ_VC_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" - # RabbitMQ properties for Edge queues - edge: "${TB_QUEUE_RABBIT_MQ_EDGE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" partitions: hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 transport_api: @@ -1697,7 +1581,7 @@ queue: # Interval in milliseconds to poll api response from transport microservices response_poll_interval: "${TB_QUEUE_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}" core: - # Default topic name of Kafka, RabbitMQ, etc. queue + # Default topic name topic: "${TB_QUEUE_CORE_TOPIC:tb_core}" # Interval in milliseconds to poll messages by Core microservices poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}" @@ -1714,7 +1598,7 @@ queue: pack-interval-ms: "${TB_QUEUE_CORE_OTA_PACK_INTERVAL_MS:60000}" # The size of OTA updates notifications fetched from the queue. The queue stores pairs of firmware and device ids pack-size: "${TB_QUEUE_CORE_OTA_PACK_SIZE:100}" - # Stats topic name for queue Kafka, RabbitMQ, etc. + # Stats topic name usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}" stats: # Enable/disable statistics for Core microservices @@ -1745,7 +1629,7 @@ queue: print-interval-ms: "${TB_HOUSEKEEPER_STATS_PRINT_INTERVAL_MS:60000}" vc: - # Default topic name for Kafka, RabbitMQ, etc. + # Default topic name topic: "${TB_QUEUE_VC_TOPIC:tb_version_control}" # Number of partitions to associate with this queue. Used for scaling the number of messages that can be processed in parallel partitions: "${TB_QUEUE_VC_PARTITIONS:10}" @@ -1755,7 +1639,7 @@ queue: pack-processing-timeout: "${TB_QUEUE_VC_PACK_PROCESSING_TIMEOUT_MS:180000}" # Timeout for a request to VC-executor (for a request for the version of the entity, for a commit charge, etc.) request-timeout: "${TB_QUEUE_VC_REQUEST_TIMEOUT:180000}" - # Queue settings for Kafka, RabbitMQ, etc. Limit for single message size + # Limit for single queue message size msg-chunk-size: "${TB_QUEUE_VC_MSG_CHUNK_SIZE:250000}" js: # JS Eval request topic @@ -1796,7 +1680,7 @@ queue: # Interval in milliseconds to poll messages poll_interval: "${TB_QUEUE_TRANSPORT_NOTIFICATIONS_POLL_INTERVAL_MS:25}" edge: - # Default topic name for Kafka, RabbitMQ, etc. + # Default topic name topic: "${TB_QUEUE_EDGE_TOPIC:tb_edge}" # Amount of partitions used by Edge services partitions: "${TB_QUEUE_EDGE_PARTITIONS:10}" diff --git a/common/queue/pom.xml b/common/queue/pom.xml index 7485294f6e..a35c443972 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -64,18 +64,10 @@ org.apache.kafka kafka-clients - - com.amazonaws - aws-java-sdk-sqs - com.google.cloud google-cloud-pubsub - - com.microsoft.azure - azure-servicebus - com.rabbitmq amqp-client diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/RuleEngineTbQueueAdminFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/RuleEngineTbQueueAdminFactory.java index 87ad934c63..057351a449 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/RuleEngineTbQueueAdminFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/RuleEngineTbQueueAdminFactory.java @@ -19,21 +19,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusAdmin; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusQueueConfigs; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusSettings; import org.thingsboard.server.queue.kafka.TbKafkaAdmin; import org.thingsboard.server.queue.kafka.TbKafkaSettings; import org.thingsboard.server.queue.kafka.TbKafkaTopicConfigs; -import org.thingsboard.server.queue.pubsub.TbPubSubAdmin; -import org.thingsboard.server.queue.pubsub.TbPubSubSettings; -import org.thingsboard.server.queue.pubsub.TbPubSubSubscriptionSettings; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqAdmin; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqQueueArguments; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqSettings; -import org.thingsboard.server.queue.sqs.TbAwsSqsAdmin; -import org.thingsboard.server.queue.sqs.TbAwsSqsQueueAttributes; -import org.thingsboard.server.queue.sqs.TbAwsSqsSettings; @Configuration public class RuleEngineTbQueueAdminFactory { @@ -43,56 +31,12 @@ public class RuleEngineTbQueueAdminFactory { @Autowired(required = false) private TbKafkaSettings kafkaSettings; - @Autowired(required = false) - private TbAwsSqsQueueAttributes awsSqsQueueAttributes; - @Autowired(required = false) - private TbAwsSqsSettings awsSqsSettings; - - @Autowired(required = false) - private TbPubSubSubscriptionSettings pubSubSubscriptionSettings; - @Autowired(required = false) - private TbPubSubSettings pubSubSettings; - - @Autowired(required = false) - private TbRabbitMqQueueArguments rabbitMqQueueArguments; - @Autowired(required = false) - private TbRabbitMqSettings rabbitMqSettings; - - @Autowired(required = false) - private TbServiceBusQueueConfigs serviceBusQueueConfigs; - @Autowired(required = false) - private TbServiceBusSettings serviceBusSettings; - @ConditionalOnExpression("'${queue.type:null}'=='kafka'") @Bean public TbQueueAdmin createKafkaAdmin() { return new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getRuleEngineConfigs()); } - @ConditionalOnExpression("'${queue.type:null}'=='aws-sqs'") - @Bean - public TbQueueAdmin createAwsSqsAdmin() { - return new TbAwsSqsAdmin(awsSqsSettings, awsSqsQueueAttributes.getRuleEngineAttributes()); - } - - @ConditionalOnExpression("'${queue.type:null}'=='pubsub'") - @Bean - public TbQueueAdmin createPubSubAdmin() { - return new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getRuleEngineSettings()); - } - - @ConditionalOnExpression("'${queue.type:null}'=='rabbitmq'") - @Bean - public TbQueueAdmin createRabbitMqAdmin() { - return new TbRabbitMqAdmin(rabbitMqSettings, rabbitMqQueueArguments.getRuleEngineArgs()); - } - - @ConditionalOnExpression("'${queue.type:null}'=='service-bus'") - @Bean - public TbQueueAdmin createServiceBusAdmin() { - return new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getRuleEngineConfigs()); - } - @ConditionalOnExpression("'${queue.type:null}'=='in-memory'") @Bean public TbQueueAdmin createInMemoryAdmin() { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusAdmin.java b/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusAdmin.java deleted file mode 100644 index 07561e7452..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusAdmin.java +++ /dev/null @@ -1,136 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.azure.servicebus; - -import com.microsoft.azure.servicebus.management.ManagementClient; -import com.microsoft.azure.servicebus.management.QueueDescription; -import com.microsoft.azure.servicebus.primitives.ConnectionStringBuilder; -import com.microsoft.azure.servicebus.primitives.MessagingEntityAlreadyExistsException; -import com.microsoft.azure.servicebus.primitives.ServiceBusException; -import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.util.PropertyUtils; - -import java.io.IOException; -import java.time.Duration; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -@Slf4j -public class TbServiceBusAdmin implements TbQueueAdmin { - private final String MAX_SIZE = "maxSizeInMb"; - private final String MESSAGE_TIME_TO_LIVE = "messageTimeToLiveInSec"; - private final String LOCK_DURATION = "lockDurationInSec"; - - private final Map queueConfigs; - private final Set queues = ConcurrentHashMap.newKeySet(); - - private final ManagementClient client; - - public TbServiceBusAdmin(TbServiceBusSettings serviceBusSettings, Map queueConfigs) { - this.queueConfigs = queueConfigs; - - ConnectionStringBuilder builder = new ConnectionStringBuilder( - serviceBusSettings.getNamespaceName(), - "queues", - serviceBusSettings.getSasKeyName(), - serviceBusSettings.getSasKey()); - - client = new ManagementClient(builder); - - try { - client.getQueues().forEach(queueDescription -> queues.add(queueDescription.getPath())); - } catch (ServiceBusException | InterruptedException e) { - log.error("Failed to get queues.", e); - throw new RuntimeException("Failed to get queues.", e); - } - } - - @Override - public void createTopicIfNotExists(String topic, String properties) { - if (queues.contains(topic)) { - return; - } - - try { - QueueDescription queueDescription = new QueueDescription(topic); - queueDescription.setRequiresDuplicateDetection(false); - setQueueConfigs(queueDescription, PropertyUtils.getProps(queueConfigs, properties)); - - client.createQueue(queueDescription); - queues.add(topic); - } catch (ServiceBusException | InterruptedException e) { - if (e instanceof MessagingEntityAlreadyExistsException) { - queues.add(topic); - log.info("[{}] queue already exists.", topic); - } else { - log.error("Failed to create queue: [{}]", topic, e); - } - } - } - - @Override - public void deleteTopic(String topic) { - if (queues.contains(topic)) { - doDelete(topic); - } else { - try { - if (client.getQueue(topic) != null) { - doDelete(topic); - } else { - log.warn("Azure Service Bus Queue [{}] is not exist.", topic); - } - } catch (ServiceBusException | InterruptedException e) { - log.error("Failed to delete Azure Service Bus queue [{}]", topic, e); - } - } - } - - private void doDelete(String topic) { - try { - client.deleteTopic(topic); - } catch (ServiceBusException | InterruptedException e) { - log.error("Failed to delete Azure Service Bus queue [{}]", topic, e); - } - } - - private void setQueueConfigs(QueueDescription queueDescription, Map queueConfigs) { - queueConfigs.forEach((confKey, confValue) -> { - switch (confKey) { - case MAX_SIZE: - queueDescription.setMaxSizeInMB(Long.parseLong(confValue)); - break; - case MESSAGE_TIME_TO_LIVE: - queueDescription.setDefaultMessageTimeToLive(Duration.ofSeconds(Long.parseLong(confValue))); - break; - case LOCK_DURATION: - queueDescription.setLockDuration(Duration.ofSeconds(Long.parseLong(confValue))); - break; - default: - log.error("Unknown config: [{}]", confKey); - } - }); - } - - public void destroy() { - try { - client.close(); - } catch (IOException e) { - log.error("Failed to close ManagementClient."); - } - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusConsumerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusConsumerTemplate.java deleted file mode 100644 index 8e2b32aab5..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusConsumerTemplate.java +++ /dev/null @@ -1,175 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.azure.servicebus; - -import com.google.gson.Gson; -import com.google.protobuf.InvalidProtocolBufferException; -import com.microsoft.azure.servicebus.TransactionContext; -import com.microsoft.azure.servicebus.primitives.ConnectionStringBuilder; -import com.microsoft.azure.servicebus.primitives.CoreMessageReceiver; -import com.microsoft.azure.servicebus.primitives.MessageWithDeliveryTag; -import com.microsoft.azure.servicebus.primitives.MessagingEntityType; -import com.microsoft.azure.servicebus.primitives.MessagingFactory; -import com.microsoft.azure.servicebus.primitives.SettleModePair; -import lombok.extern.slf4j.Slf4j; -import org.apache.qpid.proton.amqp.messaging.Data; -import org.apache.qpid.proton.amqp.transport.ReceiverSettleMode; -import org.apache.qpid.proton.amqp.transport.SenderSettleMode; -import org.springframework.util.CollectionUtils; -import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueMsg; -import org.thingsboard.server.queue.TbQueueMsgDecoder; -import org.thingsboard.server.queue.common.AbstractTbQueueConsumerTemplate; -import org.thingsboard.server.queue.common.DefaultTbQueueMsg; - -import java.time.Duration; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutionException; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -@Slf4j -public class TbServiceBusConsumerTemplate extends AbstractTbQueueConsumerTemplate { - private final TbQueueAdmin admin; - private final TbQueueMsgDecoder decoder; - private final TbServiceBusSettings serviceBusSettings; - - private final Gson gson = new Gson(); - - private Set receivers; - private final Map> pendingMessages = new ConcurrentHashMap<>(); - private volatile int messagesPerQueue; - - public TbServiceBusConsumerTemplate(TbQueueAdmin admin, TbServiceBusSettings serviceBusSettings, String topic, TbQueueMsgDecoder decoder) { - super(topic); - this.admin = admin; - this.decoder = decoder; - this.serviceBusSettings = serviceBusSettings; - } - - @Override - protected List doPoll(long durationInMillis) { - List>> messageFutures = - receivers.stream() - .map(receiver -> receiver - .receiveAsync(messagesPerQueue, Duration.ofMillis(durationInMillis)) - .whenComplete((messages, err) -> { - if (!CollectionUtils.isEmpty(messages)) { - pendingMessages.put(receiver, messages); - } else if (err != null) { - log.error("Failed to receive messages.", err); - } - })) - .collect(Collectors.toList()); - try { - return fromList(messageFutures) - .get() - .stream() - .flatMap(messages -> CollectionUtils.isEmpty(messages) ? Stream.empty() : messages.stream()) - .collect(Collectors.toList()); - } catch (InterruptedException | ExecutionException e) { - if (stopped) { - log.info("[{}] Service Bus consumer is stopped.", getTopic()); - } else { - log.error("Failed to receive messages", e); - } - return Collections.emptyList(); - } - } - - @Override - protected void doSubscribe(List topicNames) { - createReceivers(); - messagesPerQueue = receivers.size() / Math.max(partitions.size(), 1); - } - - @Override - protected void doCommit() { - pendingMessages.forEach((receiver, msgs) -> - msgs.forEach(msg -> receiver.completeMessageAsync(msg.getDeliveryTag(), TransactionContext.NULL_TXN))); - pendingMessages.clear(); - } - - @Override - protected void doUnsubscribe() { - receivers.forEach(CoreMessageReceiver::closeAsync); - } - - private void createReceivers() { - List> receiverFutures = partitions.stream() - .map(TopicPartitionInfo::getFullTopicName) - .map(queue -> { - MessagingFactory factory; - try { - factory = MessagingFactory.createFromConnectionStringBuilder(createConnection(queue)); - } catch (InterruptedException | ExecutionException e) { - log.error("Failed to create factory for the queue [{}]", queue); - throw new RuntimeException("Failed to create the factory", e); - } - - return CoreMessageReceiver.create(factory, queue, queue, 0, - new SettleModePair(SenderSettleMode.UNSETTLED, ReceiverSettleMode.SECOND), - MessagingEntityType.QUEUE); - }).collect(Collectors.toList()); - - try { - receivers = new HashSet<>(fromList(receiverFutures).get()); - } catch (InterruptedException | ExecutionException e) { - if (stopped) { - log.info("[{}] Service Bus consumer is stopped.", getTopic()); - } else { - log.error("Failed to create receivers", e); - } - } - } - - private ConnectionStringBuilder createConnection(String queue) { - admin.createTopicIfNotExists(queue); - return new ConnectionStringBuilder( - serviceBusSettings.getNamespaceName(), - queue, - serviceBusSettings.getSasKeyName(), - serviceBusSettings.getSasKey()); - } - - private CompletableFuture> fromList(List> futures) { - @SuppressWarnings("unchecked") - CompletableFuture>[] arrayFuture = new CompletableFuture[futures.size()]; - futures.toArray(arrayFuture); - - return CompletableFuture - .allOf(arrayFuture) - .thenApply(v -> futures - .stream() - .map(CompletableFuture::join) - .collect(Collectors.toList())); - } - - @Override - protected T decode(MessageWithDeliveryTag data) throws InvalidProtocolBufferException { - DefaultTbQueueMsg msg = gson.fromJson(new String(((Data) data.getMessage().getBody()).getValue().getArray()), DefaultTbQueueMsg.class); - return decoder.decode(msg); - } - -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusProducerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusProducerTemplate.java deleted file mode 100644 index 119f28079b..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusProducerTemplate.java +++ /dev/null @@ -1,110 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.azure.servicebus; - -import com.google.gson.Gson; -import com.microsoft.azure.servicebus.IMessage; -import com.microsoft.azure.servicebus.Message; -import com.microsoft.azure.servicebus.QueueClient; -import com.microsoft.azure.servicebus.ReceiveMode; -import com.microsoft.azure.servicebus.primitives.ConnectionStringBuilder; -import com.microsoft.azure.servicebus.primitives.ServiceBusException; -import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueCallback; -import org.thingsboard.server.queue.TbQueueMsg; -import org.thingsboard.server.queue.TbQueueProducer; -import org.thingsboard.server.queue.common.DefaultTbQueueMsg; - -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -@Slf4j -public class TbServiceBusProducerTemplate implements TbQueueProducer { - private final String defaultTopic; - private final Gson gson = new Gson(); - private final TbQueueAdmin admin; - private final TbServiceBusSettings serviceBusSettings; - private final Map clients = new ConcurrentHashMap<>(); - private final ExecutorService executorService; - - public TbServiceBusProducerTemplate(TbQueueAdmin admin, TbServiceBusSettings serviceBusSettings, String defaultTopic) { - this.admin = admin; - this.defaultTopic = defaultTopic; - this.serviceBusSettings = serviceBusSettings; - executorService = Executors.newCachedThreadPool(); - } - - @Override - public void init() { - - } - - @Override - public String getDefaultTopic() { - return defaultTopic; - } - - @Override - public void send(TopicPartitionInfo tpi, T msg, TbQueueCallback callback) { - IMessage message = new Message(gson.toJson(new DefaultTbQueueMsg(msg))); - CompletableFuture future = getClient(tpi.getFullTopicName()).sendAsync(message); - future.whenCompleteAsync((success, err) -> { - if (err != null) { - callback.onFailure(err); - } else { - callback.onSuccess(null); - } - }, executorService); - } - - @Override - public void stop() { - clients.forEach((t, client) -> { - try { - client.close(); - } catch (ServiceBusException e) { - log.error("Failed to close QueueClient.", e); - } - }); - - if (executorService != null) { - executorService.shutdownNow(); - } - } - - private QueueClient getClient(String topic) { - return clients.computeIfAbsent(topic, k -> { - admin.createTopicIfNotExists(topic); - ConnectionStringBuilder builder = - new ConnectionStringBuilder( - serviceBusSettings.getNamespaceName(), - topic, - serviceBusSettings.getSasKeyName(), - serviceBusSettings.getSasKey()); - try { - return new QueueClient(builder, ReceiveMode.PEEKLOCK); - } catch (InterruptedException | ServiceBusException e) { - log.error("Failed to create new client for the Queue: [{}]", topic, e); - throw new RuntimeException("Failed to create new client for the Queue", e); - } - }); - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusQueueConfigs.java b/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusQueueConfigs.java deleted file mode 100644 index 80da7846ea..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusQueueConfigs.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.azure.servicebus; - -import jakarta.annotation.PostConstruct; -import lombok.Getter; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.stereotype.Component; -import org.thingsboard.server.queue.util.PropertyUtils; - -import java.util.Map; - -@Component -@ConditionalOnExpression("'${queue.type:null}'=='service-bus'") -public class TbServiceBusQueueConfigs { - - @Value("${queue.service-bus.queue-properties.core:}") - private String coreProperties; - @Value("${queue.service-bus.queue-properties.rule-engine:}") - private String ruleEngineProperties; - @Value("${queue.service-bus.queue-properties.transport-api:}") - private String transportApiProperties; - @Value("${queue.service-bus.queue-properties.notifications:}") - private String notificationsProperties; - @Value("${queue.service-bus.queue-properties.js-executor:}") - private String jsExecutorProperties; - @Value("${queue.service-bus.queue-properties.version-control:}") - private String vcProperties; - @Value("${queue.service-bus.queue-properties.edge:}") - private String edgeProperties; - - @Getter - private Map coreConfigs; - @Getter - private Map ruleEngineConfigs; - @Getter - private Map transportApiConfigs; - @Getter - private Map notificationsConfigs; - @Getter - private Map jsExecutorConfigs; - @Getter - private Map vcConfigs; - @Getter - private Map edgeConfigs; - - @PostConstruct - private void init() { - coreConfigs = PropertyUtils.getProps(coreProperties); - ruleEngineConfigs = PropertyUtils.getProps(ruleEngineProperties); - transportApiConfigs = PropertyUtils.getProps(transportApiProperties); - notificationsConfigs = PropertyUtils.getProps(notificationsProperties); - jsExecutorConfigs = PropertyUtils.getProps(jsExecutorProperties); - vcConfigs = PropertyUtils.getProps(vcProperties); - edgeConfigs = PropertyUtils.getProps(edgeProperties); - } - -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusSettings.java b/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusSettings.java deleted file mode 100644 index 98bb11a839..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusSettings.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.azure.servicebus; - -import lombok.Data; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.stereotype.Component; - -@Slf4j -@ConditionalOnExpression("'${queue.type:null}'=='service-bus'") -@Component -@Data -public class TbServiceBusSettings { - @Value("${queue.service_bus.namespace_name}") - private String namespaceName; - @Value("${queue.service_bus.sas_key_name}") - private String sasKeyName; - @Value("${queue.service_bus.sas_key}") - private String sasKey; - @Value("${queue.service_bus.max_messages}") - private int maxMessages; -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsMonolithQueueFactory.java deleted file mode 100644 index 005e9aee17..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsMonolithQueueFactory.java +++ /dev/null @@ -1,311 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.provider; - -import com.google.protobuf.util.JsonFormat; -import jakarta.annotation.PreDestroy; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.context.annotation.Bean; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.queue.Queue; -import org.thingsboard.server.common.msg.queue.ServiceType; -import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsRequest; -import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsResponse; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToEdgeMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToEdgeNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToHousekeeperServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToVersionControlServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueConsumer; -import org.thingsboard.server.queue.TbQueueProducer; -import org.thingsboard.server.queue.TbQueueRequestTemplate; -import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; -import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; -import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; -import org.thingsboard.server.queue.discovery.TopicService; -import org.thingsboard.server.queue.settings.TbQueueCoreSettings; -import org.thingsboard.server.queue.settings.TbQueueEdgeSettings; -import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings; -import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; -import org.thingsboard.server.queue.settings.TbQueueVersionControlSettings; -import org.thingsboard.server.queue.sqs.TbAwsSqsAdmin; -import org.thingsboard.server.queue.sqs.TbAwsSqsConsumerTemplate; -import org.thingsboard.server.queue.sqs.TbAwsSqsProducerTemplate; -import org.thingsboard.server.queue.sqs.TbAwsSqsQueueAttributes; -import org.thingsboard.server.queue.sqs.TbAwsSqsSettings; - -import java.nio.charset.StandardCharsets; - -@Component -@ConditionalOnExpression("'${queue.type:null}'=='aws-sqs' && '${service.type:null}'=='monolith'") -public class AwsSqsMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngineQueueFactory, TbVersionControlQueueFactory { - - private final TopicService topicService; - private final TbQueueCoreSettings coreSettings; - private final TbServiceInfoProvider serviceInfoProvider; - private final TbQueueRuleEngineSettings ruleEngineSettings; - private final TbQueueTransportApiSettings transportApiSettings; - private final TbQueueTransportNotificationSettings transportNotificationSettings; - private final TbAwsSqsSettings sqsSettings; - private final TbQueueVersionControlSettings vcSettings; - private final TbQueueEdgeSettings edgeSettings; - private final TbQueueRemoteJsInvokeSettings jsInvokeSettings; - - private final TbQueueAdmin coreAdmin; - private final TbQueueAdmin ruleEngineAdmin; - private final TbQueueAdmin jsExecutorAdmin; - private final TbQueueAdmin transportApiAdmin; - private final TbQueueAdmin notificationAdmin; - private final TbQueueAdmin otaAdmin; - private final TbQueueAdmin vcAdmin; - private final TbQueueAdmin edgeAdmin; - - public AwsSqsMonolithQueueFactory(TopicService topicService, TbQueueCoreSettings coreSettings, - TbQueueRuleEngineSettings ruleEngineSettings, - TbServiceInfoProvider serviceInfoProvider, - TbQueueTransportApiSettings transportApiSettings, - TbQueueTransportNotificationSettings transportNotificationSettings, - TbAwsSqsSettings sqsSettings, - TbQueueVersionControlSettings vcSettings, - TbQueueEdgeSettings edgeSettings, - TbAwsSqsQueueAttributes sqsQueueAttributes, - TbQueueRemoteJsInvokeSettings jsInvokeSettings) { - this.topicService = topicService; - this.coreSettings = coreSettings; - this.serviceInfoProvider = serviceInfoProvider; - this.ruleEngineSettings = ruleEngineSettings; - this.transportApiSettings = transportApiSettings; - this.transportNotificationSettings = transportNotificationSettings; - this.sqsSettings = sqsSettings; - this.vcSettings = vcSettings; - this.edgeSettings = edgeSettings; - this.jsInvokeSettings = jsInvokeSettings; - - this.coreAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getCoreAttributes()); - this.ruleEngineAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getRuleEngineAttributes()); - this.jsExecutorAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getJsExecutorAttributes()); - this.transportApiAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getTransportApiAttributes()); - this.notificationAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getNotificationsAttributes()); - this.otaAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getOtaAttributes()); - this.vcAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getVcAttributes()); - this.edgeAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getEdgeAttributes()); - } - - @Override - public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); - } - - @Override - public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbAwsSqsProducerTemplate<>(ruleEngineAdmin, sqsSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); - } - - @Override - public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, - topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName()); - } - - @Override - public TbQueueConsumer> createToVersionControlMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(vcAdmin, sqsSettings, topicService.buildTopicName(vcSettings.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders()) - ); - } - - @Override - public TbQueueConsumer> createToRuleEngineMsgConsumer(Queue configuration) { - return new TbAwsSqsConsumerTemplate<>(ruleEngineAdmin, sqsSettings, topicService.buildTopicName(configuration.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToRuleEngineNotificationsMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(notificationAdmin, sqsSettings, - topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToCoreMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToCoreNotificationsMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(notificationAdmin, sqsSettings, - topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createTransportApiRequestConsumer() { - return new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createTransportApiResponseProducer() { - return new TbAwsSqsProducerTemplate<>(transportApiAdmin, sqsSettings, topicService.buildTopicName(transportApiSettings.getResponsesTopic())); - } - - @Override - @Bean - public TbQueueRequestTemplate, TbProtoQueueMsg> createRemoteJsRequestTemplate() { - TbQueueProducer> producer = new TbAwsSqsProducerTemplate<>(jsExecutorAdmin, sqsSettings, jsInvokeSettings.getRequestTopic()); - TbQueueConsumer> consumer = new TbAwsSqsConsumerTemplate<>(jsExecutorAdmin, sqsSettings, - jsInvokeSettings.getResponseTopic() + "_" + serviceInfoProvider.getServiceId(), - msg -> { - RemoteJsResponse.Builder builder = RemoteJsResponse.newBuilder(); - JsonFormat.parser().ignoringUnknownFields().merge(new String(msg.getData(), StandardCharsets.UTF_8), builder); - return new TbProtoQueueMsg<>(msg.getKey(), builder.build(), msg.getHeaders()); - }); - - DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder - , TbProtoQueueMsg> builder = DefaultTbQueueRequestTemplate.builder(); - builder.queueAdmin(jsExecutorAdmin); - builder.requestTemplate(producer); - builder.responseTemplate(consumer); - builder.maxPendingRequests(jsInvokeSettings.getMaxPendingRequests()); - builder.maxRequestTimeout(jsInvokeSettings.getMaxRequestsTimeout()); - builder.pollInterval(jsInvokeSettings.getResponsePollInterval()); - return builder.build(); - } - - @Override - public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); - } - - @Override - public TbQueueConsumer> createToUsageStatsServiceMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(otaAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbAwsSqsProducerTemplate<>(otaAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); - } - - @Override - public TbQueueProducer> createVersionControlMsgProducer() { - return new TbAwsSqsProducerTemplate<>(vcAdmin, sqsSettings, topicService.buildTopicName(vcSettings.getTopic())); - } - - public TbQueueProducer> createHousekeeperMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic())); - } - - @Override - public TbQueueConsumer> createHousekeeperMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToHousekeeperServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createHousekeeperReprocessingMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getHousekeeperReprocessingTopic())); - } - - @Override - public TbQueueConsumer> createHousekeeperReprocessingMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getHousekeeperReprocessingTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToHousekeeperServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createEdgeMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(edgeAdmin, sqsSettings, topicService.buildTopicName(edgeSettings.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToEdgeMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createEdgeMsgProducer() { - return new TbAwsSqsProducerTemplate<>(edgeAdmin, sqsSettings, topicService.buildTopicName(edgeSettings.getTopic())); - } - - @Override - public TbQueueConsumer> createToEdgeNotificationsMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(notificationAdmin, sqsSettings, - topicService.getEdgeNotificationsTopic(serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToEdgeNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createEdgeNotificationsMsgProducer() { - return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, - topicService.getEdgeNotificationsTopic(serviceInfoProvider.getServiceId()).getFullTopicName()); - } - - @PreDestroy - private void destroy() { - if (coreAdmin != null) { - coreAdmin.destroy(); - } - if (ruleEngineAdmin != null) { - ruleEngineAdmin.destroy(); - } - if (jsExecutorAdmin != null) { - jsExecutorAdmin.destroy(); - } - if (transportApiAdmin != null) { - transportApiAdmin.destroy(); - } - if (notificationAdmin != null) { - notificationAdmin.destroy(); - } - if (otaAdmin != null) { - otaAdmin.destroy(); - } - if (vcAdmin != null) { - vcAdmin.destroy(); - } - if (edgeAdmin != null) { - edgeAdmin.destroy(); - } - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbCoreQueueFactory.java deleted file mode 100644 index 515c1bba44..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbCoreQueueFactory.java +++ /dev/null @@ -1,291 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.provider; - -import com.google.protobuf.util.JsonFormat; -import jakarta.annotation.PreDestroy; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.context.annotation.Bean; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.msg.queue.ServiceType; -import org.thingsboard.server.gen.js.JsInvokeProtos; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToEdgeMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToEdgeNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToHousekeeperServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToVersionControlServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueConsumer; -import org.thingsboard.server.queue.TbQueueProducer; -import org.thingsboard.server.queue.TbQueueRequestTemplate; -import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; -import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; -import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; -import org.thingsboard.server.queue.discovery.TopicService; -import org.thingsboard.server.queue.settings.TbQueueCoreSettings; -import org.thingsboard.server.queue.settings.TbQueueEdgeSettings; -import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings; -import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; -import org.thingsboard.server.queue.settings.TbQueueVersionControlSettings; -import org.thingsboard.server.queue.sqs.TbAwsSqsAdmin; -import org.thingsboard.server.queue.sqs.TbAwsSqsConsumerTemplate; -import org.thingsboard.server.queue.sqs.TbAwsSqsProducerTemplate; -import org.thingsboard.server.queue.sqs.TbAwsSqsQueueAttributes; -import org.thingsboard.server.queue.sqs.TbAwsSqsSettings; - -import java.nio.charset.StandardCharsets; - -@Component -@ConditionalOnExpression("'${queue.type:null}'=='aws-sqs' && '${service.type:null}'=='tb-core'") -public class AwsSqsTbCoreQueueFactory implements TbCoreQueueFactory { - - private final TbAwsSqsSettings sqsSettings; - private final TbQueueRuleEngineSettings ruleEngineSettings; - private final TbQueueCoreSettings coreSettings; - private final TbQueueTransportApiSettings transportApiSettings; - private final TopicService topicService; - private final TbServiceInfoProvider serviceInfoProvider; - private final TbQueueRemoteJsInvokeSettings jsInvokeSettings; - private final TbQueueTransportNotificationSettings transportNotificationSettings; - private final TbQueueVersionControlSettings vcSettings; - private final TbQueueEdgeSettings edgeSettings; - - private final TbQueueAdmin coreAdmin; - private final TbQueueAdmin ruleEngineAdmin; - private final TbQueueAdmin jsExecutorAdmin; - private final TbQueueAdmin transportApiAdmin; - private final TbQueueAdmin notificationAdmin; - private final TbQueueAdmin otaAdmin; - private final TbQueueAdmin vcAdmin; - private final TbQueueAdmin edgeAdmin; - - public AwsSqsTbCoreQueueFactory(TbAwsSqsSettings sqsSettings, - TbQueueCoreSettings coreSettings, - TbQueueTransportApiSettings transportApiSettings, - TbQueueRuleEngineSettings ruleEngineSettings, - TopicService topicService, - TbQueueVersionControlSettings vcSettings, - TbQueueEdgeSettings edgeSettings, - TbServiceInfoProvider serviceInfoProvider, - TbQueueRemoteJsInvokeSettings jsInvokeSettings, - TbAwsSqsQueueAttributes sqsQueueAttributes, - TbQueueTransportNotificationSettings transportNotificationSettings) { - this.sqsSettings = sqsSettings; - this.coreSettings = coreSettings; - this.transportApiSettings = transportApiSettings; - this.ruleEngineSettings = ruleEngineSettings; - this.edgeSettings = edgeSettings; - this.topicService = topicService; - this.serviceInfoProvider = serviceInfoProvider; - this.jsInvokeSettings = jsInvokeSettings; - this.transportNotificationSettings = transportNotificationSettings; - this.vcSettings = vcSettings; - - this.coreAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getCoreAttributes()); - this.ruleEngineAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getRuleEngineAttributes()); - this.jsExecutorAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getJsExecutorAttributes()); - this.transportApiAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getTransportApiAttributes()); - this.notificationAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getNotificationsAttributes()); - this.otaAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getOtaAttributes()); - this.vcAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getVcAttributes()); - this.edgeAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getEdgeAttributes()); - } - - @Override - public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); - } - - @Override - public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, - topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName()); - } - - @Override - public TbQueueConsumer> createToCoreMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToCoreNotificationsMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(notificationAdmin, sqsSettings, - topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createTransportApiRequestConsumer() { - return new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createTransportApiResponseProducer() { - return new TbAwsSqsProducerTemplate<>(transportApiAdmin, sqsSettings, topicService.buildTopicName(transportApiSettings.getResponsesTopic())); - } - - @Override - @Bean - public TbQueueRequestTemplate, TbProtoQueueMsg> createRemoteJsRequestTemplate() { - TbQueueProducer> producer = new TbAwsSqsProducerTemplate<>(jsExecutorAdmin, sqsSettings, jsInvokeSettings.getRequestTopic()); - TbQueueConsumer> consumer = new TbAwsSqsConsumerTemplate<>(jsExecutorAdmin, sqsSettings, - jsInvokeSettings.getResponseTopic() + "_" + serviceInfoProvider.getServiceId(), - msg -> { - JsInvokeProtos.RemoteJsResponse.Builder builder = JsInvokeProtos.RemoteJsResponse.newBuilder(); - JsonFormat.parser().ignoringUnknownFields().merge(new String(msg.getData(), StandardCharsets.UTF_8), builder); - return new TbProtoQueueMsg<>(msg.getKey(), builder.build(), msg.getHeaders()); - }); - - DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder - , TbProtoQueueMsg> builder = DefaultTbQueueRequestTemplate.builder(); - builder.queueAdmin(jsExecutorAdmin); - builder.requestTemplate(producer); - builder.responseTemplate(consumer); - builder.maxPendingRequests(jsInvokeSettings.getMaxPendingRequests()); - builder.maxRequestTimeout(jsInvokeSettings.getMaxRequestsTimeout()); - builder.pollInterval(jsInvokeSettings.getResponsePollInterval()); - return builder.build(); - } - - @Override - public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); - } - - @Override - public TbQueueConsumer> createToUsageStatsServiceMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(otaAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbAwsSqsProducerTemplate<>(otaAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); - } - - @Override - public TbQueueProducer> createVersionControlMsgProducer() { - return new TbAwsSqsProducerTemplate<>(vcAdmin, sqsSettings, topicService.buildTopicName(vcSettings.getTopic())); - } - - @Override - public TbQueueProducer> createHousekeeperMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic())); - } - - @Override - public TbQueueConsumer> createHousekeeperMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToHousekeeperServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createHousekeeperReprocessingMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getHousekeeperReprocessingTopic())); - } - - @Override - public TbQueueConsumer> createHousekeeperReprocessingMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getHousekeeperReprocessingTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToHousekeeperServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createEdgeMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(edgeAdmin, sqsSettings, topicService.buildTopicName(edgeSettings.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToEdgeMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createEdgeMsgProducer() { - return new TbAwsSqsProducerTemplate<>(edgeAdmin, sqsSettings, topicService.buildTopicName(edgeSettings.getTopic())); - } - - @Override - public TbQueueConsumer> createToEdgeNotificationsMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(notificationAdmin, sqsSettings, - topicService.getEdgeNotificationsTopic(serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToEdgeNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createEdgeNotificationsMsgProducer() { - return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, - topicService.getEdgeNotificationsTopic(serviceInfoProvider.getServiceId()).getFullTopicName()); - } - - @PreDestroy - private void destroy() { - if (coreAdmin != null) { - coreAdmin.destroy(); - } - if (ruleEngineAdmin != null) { - ruleEngineAdmin.destroy(); - } - if (jsExecutorAdmin != null) { - jsExecutorAdmin.destroy(); - } - if (transportApiAdmin != null) { - transportApiAdmin.destroy(); - } - if (notificationAdmin != null) { - notificationAdmin.destroy(); - } - if (otaAdmin != null) { - otaAdmin.destroy(); - } - if (vcAdmin != null) { - vcAdmin.destroy(); - } - if (edgeAdmin != null) { - edgeAdmin.destroy(); - } - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbRuleEngineQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbRuleEngineQueueFactory.java deleted file mode 100644 index 37319e378b..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbRuleEngineQueueFactory.java +++ /dev/null @@ -1,208 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.provider; - -import com.google.protobuf.util.JsonFormat; -import jakarta.annotation.PreDestroy; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.context.annotation.Bean; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.queue.Queue; -import org.thingsboard.server.common.msg.queue.ServiceType; -import org.thingsboard.server.gen.js.JsInvokeProtos; -import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToEdgeMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToHousekeeperServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueConsumer; -import org.thingsboard.server.queue.TbQueueProducer; -import org.thingsboard.server.queue.TbQueueRequestTemplate; -import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; -import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; -import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; -import org.thingsboard.server.queue.discovery.TopicService; -import org.thingsboard.server.queue.settings.TbQueueCoreSettings; -import org.thingsboard.server.queue.settings.TbQueueEdgeSettings; -import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings; -import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; -import org.thingsboard.server.queue.sqs.TbAwsSqsAdmin; -import org.thingsboard.server.queue.sqs.TbAwsSqsConsumerTemplate; -import org.thingsboard.server.queue.sqs.TbAwsSqsProducerTemplate; -import org.thingsboard.server.queue.sqs.TbAwsSqsQueueAttributes; -import org.thingsboard.server.queue.sqs.TbAwsSqsSettings; - -import java.nio.charset.StandardCharsets; - -@Component -@ConditionalOnExpression("'${queue.type:null}'=='aws-sqs' && '${service.type:null}'=='tb-rule-engine'") -public class AwsSqsTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { - - private final TopicService topicService; - private final TbQueueCoreSettings coreSettings; - private final TbServiceInfoProvider serviceInfoProvider; - private final TbQueueRuleEngineSettings ruleEngineSettings; - private final TbAwsSqsSettings sqsSettings; - private final TbQueueRemoteJsInvokeSettings jsInvokeSettings; - private final TbQueueTransportNotificationSettings transportNotificationSettings; - private final TbQueueEdgeSettings edgeSettings; - - private final TbQueueAdmin coreAdmin; - private final TbQueueAdmin ruleEngineAdmin; - private final TbQueueAdmin jsExecutorAdmin; - private final TbQueueAdmin notificationAdmin; - private final TbQueueAdmin otaAdmin; - private final TbQueueAdmin edgeAdmin; - - public AwsSqsTbRuleEngineQueueFactory(TopicService topicService, TbQueueCoreSettings coreSettings, - TbQueueRuleEngineSettings ruleEngineSettings, - TbServiceInfoProvider serviceInfoProvider, - TbAwsSqsSettings sqsSettings, - TbAwsSqsQueueAttributes sqsQueueAttributes, - TbQueueRemoteJsInvokeSettings jsInvokeSettings, - TbQueueTransportNotificationSettings transportNotificationSettings, - TbQueueEdgeSettings edgeSettings) { - this.topicService = topicService; - this.coreSettings = coreSettings; - this.serviceInfoProvider = serviceInfoProvider; - this.ruleEngineSettings = ruleEngineSettings; - this.sqsSettings = sqsSettings; - this.jsInvokeSettings = jsInvokeSettings; - this.transportNotificationSettings = transportNotificationSettings; - this.edgeSettings = edgeSettings; - - this.coreAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getCoreAttributes()); - this.ruleEngineAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getRuleEngineAttributes()); - this.jsExecutorAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getJsExecutorAttributes()); - this.notificationAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getNotificationsAttributes()); - this.otaAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getOtaAttributes()); - this.edgeAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getEdgeAttributes()); - } - - @Override - public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); - } - - @Override - public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbAwsSqsProducerTemplate<>(ruleEngineAdmin, sqsSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); - } - - @Override - public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueProducer> createEdgeMsgProducer() { - return new TbAwsSqsProducerTemplate<>(edgeAdmin, sqsSettings, topicService.buildTopicName(edgeSettings.getTopic())); - } - - @Override - public TbQueueProducer> createEdgeNotificationsMsgProducer() { - return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, topicService.getEdgeNotificationsTopic(serviceInfoProvider.getServiceId()).getFullTopicName()); - } - - @Override - public TbQueueConsumer> createToRuleEngineMsgConsumer(Queue configuration) { - return new TbAwsSqsConsumerTemplate<>(ruleEngineAdmin, sqsSettings, topicService.buildTopicName(configuration.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToRuleEngineNotificationsMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(notificationAdmin, sqsSettings, - topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - @Bean - public TbQueueRequestTemplate, TbProtoQueueMsg> createRemoteJsRequestTemplate() { - TbQueueProducer> producer = new TbAwsSqsProducerTemplate<>(jsExecutorAdmin, sqsSettings, jsInvokeSettings.getRequestTopic()); - TbQueueConsumer> consumer = new TbAwsSqsConsumerTemplate<>(jsExecutorAdmin, sqsSettings, - jsInvokeSettings.getResponseTopic() + "_" + serviceInfoProvider.getServiceId(), - msg -> { - JsInvokeProtos.RemoteJsResponse.Builder builder = JsInvokeProtos.RemoteJsResponse.newBuilder(); - JsonFormat.parser().ignoringUnknownFields().merge(new String(msg.getData(), StandardCharsets.UTF_8), builder); - return new TbProtoQueueMsg<>(msg.getKey(), builder.build(), msg.getHeaders()); - }); - - DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder - , TbProtoQueueMsg> builder = DefaultTbQueueRequestTemplate.builder(); - builder.queueAdmin(jsExecutorAdmin); - builder.requestTemplate(producer); - builder.responseTemplate(consumer); - builder.maxPendingRequests(jsInvokeSettings.getMaxPendingRequests()); - builder.maxRequestTimeout(jsInvokeSettings.getMaxRequestsTimeout()); - builder.pollInterval(jsInvokeSettings.getResponsePollInterval()); - return builder.build(); - } - - @Override - public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); - } - - @Override - public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbAwsSqsProducerTemplate<>(otaAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); - } - - @Override - public TbQueueProducer> createHousekeeperMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic())); - } - - @PreDestroy - private void destroy() { - if (coreAdmin != null) { - coreAdmin.destroy(); - } - if (ruleEngineAdmin != null) { - ruleEngineAdmin.destroy(); - } - if (jsExecutorAdmin != null) { - jsExecutorAdmin.destroy(); - } - if (notificationAdmin != null) { - notificationAdmin.destroy(); - } - if (otaAdmin != null) { - otaAdmin.destroy(); - } - } - -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbVersionControlQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbVersionControlQueueFactory.java deleted file mode 100644 index 67ff0393bd..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbVersionControlQueueFactory.java +++ /dev/null @@ -1,98 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.provider; - -import jakarta.annotation.PreDestroy; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.stereotype.Component; -import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueConsumer; -import org.thingsboard.server.queue.TbQueueProducer; -import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.TopicService; -import org.thingsboard.server.queue.settings.TbQueueCoreSettings; -import org.thingsboard.server.queue.settings.TbQueueVersionControlSettings; -import org.thingsboard.server.queue.sqs.TbAwsSqsAdmin; -import org.thingsboard.server.queue.sqs.TbAwsSqsConsumerTemplate; -import org.thingsboard.server.queue.sqs.TbAwsSqsProducerTemplate; -import org.thingsboard.server.queue.sqs.TbAwsSqsQueueAttributes; -import org.thingsboard.server.queue.sqs.TbAwsSqsSettings; - -@Component -@ConditionalOnExpression("'${queue.type:null}'=='aws-sqs' && '${service.type:null}'=='tb-vc-executor'") -public class AwsSqsTbVersionControlQueueFactory implements TbVersionControlQueueFactory { - - private final TbAwsSqsSettings sqsSettings; - private final TbQueueCoreSettings coreSettings; - private final TbQueueVersionControlSettings vcSettings; - private final TopicService topicService; - - private final TbQueueAdmin coreAdmin; - private final TbQueueAdmin notificationAdmin; - private final TbQueueAdmin vcAdmin; - - public AwsSqsTbVersionControlQueueFactory(TbAwsSqsSettings sqsSettings, - TbQueueCoreSettings coreSettings, - TbQueueVersionControlSettings vcSettings, - TbAwsSqsQueueAttributes sqsQueueAttributes, - TopicService topicService - ) { - this.sqsSettings = sqsSettings; - this.coreSettings = coreSettings; - this.vcSettings = vcSettings; - this.topicService = topicService; - - this.coreAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getCoreAttributes()); - this.notificationAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getNotificationsAttributes()); - this.vcAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getVcAttributes()); - } - - @Override - public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); - } - - @Override - public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueConsumer> createToVersionControlMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(vcAdmin, sqsSettings, topicService.buildTopicName(vcSettings.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders()) - ); - } - - @Override - public TbQueueProducer> createHousekeeperMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic())); - } - - @PreDestroy - private void destroy() { - if (coreAdmin != null) { - coreAdmin.destroy(); - } - if (notificationAdmin != null) { - notificationAdmin.destroy(); - } - if (vcAdmin != null) { - vcAdmin.destroy(); - } - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTransportQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTransportQueueFactory.java deleted file mode 100644 index 407ef86a99..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTransportQueueFactory.java +++ /dev/null @@ -1,153 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.provider; - -import jakarta.annotation.PreDestroy; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.stereotype.Component; -import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueConsumer; -import org.thingsboard.server.queue.TbQueueProducer; -import org.thingsboard.server.queue.TbQueueRequestTemplate; -import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; -import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; -import org.thingsboard.server.queue.discovery.TopicService; -import org.thingsboard.server.queue.settings.TbQueueCoreSettings; -import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; -import org.thingsboard.server.queue.sqs.TbAwsSqsAdmin; -import org.thingsboard.server.queue.sqs.TbAwsSqsConsumerTemplate; -import org.thingsboard.server.queue.sqs.TbAwsSqsProducerTemplate; -import org.thingsboard.server.queue.sqs.TbAwsSqsQueueAttributes; -import org.thingsboard.server.queue.sqs.TbAwsSqsSettings; - -@Component -@ConditionalOnExpression("'${queue.type:null}'=='aws-sqs' && (('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true') || '${service.type:null}'=='tb-transport')") -@Slf4j -public class AwsSqsTransportQueueFactory implements TbTransportQueueFactory { - private final TbQueueTransportApiSettings transportApiSettings; - private final TbQueueTransportNotificationSettings transportNotificationSettings; - private final TbAwsSqsSettings sqsSettings; - private final TbQueueCoreSettings coreSettings; - private final TbServiceInfoProvider serviceInfoProvider; - private final TbQueueRuleEngineSettings ruleEngineSettings; - private final TopicService topicService; - - private final TbQueueAdmin coreAdmin; - private final TbQueueAdmin transportApiAdmin; - private final TbQueueAdmin notificationAdmin; - private final TbQueueAdmin ruleEngineAdmin; - - public AwsSqsTransportQueueFactory(TbQueueTransportApiSettings transportApiSettings, - TbQueueTransportNotificationSettings transportNotificationSettings, - TbAwsSqsSettings sqsSettings, - TbServiceInfoProvider serviceInfoProvider, - TbQueueCoreSettings coreSettings, - TbAwsSqsQueueAttributes sqsQueueAttributes, - TbQueueRuleEngineSettings ruleEngineSettings, - TopicService topicService) { - this.transportApiSettings = transportApiSettings; - this.transportNotificationSettings = transportNotificationSettings; - this.sqsSettings = sqsSettings; - this.serviceInfoProvider = serviceInfoProvider; - this.coreSettings = coreSettings; - this.ruleEngineSettings = ruleEngineSettings; - this.topicService = topicService; - - this.coreAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getCoreAttributes()); - this.transportApiAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getTransportApiAttributes()); - this.notificationAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getNotificationsAttributes()); - this.ruleEngineAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getRuleEngineAttributes()); - } - - @Override - public TbQueueRequestTemplate, TbProtoQueueMsg> createTransportApiRequestTemplate() { - TbQueueProducer> producerTemplate = - new TbAwsSqsProducerTemplate<>(transportApiAdmin, sqsSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic())); - - TbQueueConsumer> consumerTemplate = - new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, - topicService.buildTopicName(transportApiSettings.getResponsesTopic() + "_" + serviceInfoProvider.getServiceId()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiResponseMsg.parseFrom(msg.getData()), msg.getHeaders())); - - DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder - , TbProtoQueueMsg> templateBuilder = DefaultTbQueueRequestTemplate.builder(); - templateBuilder.queueAdmin(transportApiAdmin); - templateBuilder.requestTemplate(producerTemplate); - templateBuilder.responseTemplate(consumerTemplate); - templateBuilder.maxPendingRequests(transportApiSettings.getMaxPendingRequests()); - templateBuilder.maxRequestTimeout(transportApiSettings.getMaxRequestsTimeout()); - templateBuilder.pollInterval(transportApiSettings.getResponsePollInterval()); - return templateBuilder.build(); - } - - @Override - public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbAwsSqsProducerTemplate<>(ruleEngineAdmin, sqsSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueConsumer> createTransportNotificationsConsumer() { - return new TbAwsSqsConsumerTemplate<>(notificationAdmin, sqsSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic() + "_" + serviceInfoProvider.getServiceId()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToTransportMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); - } - - @Override - public TbQueueProducer> createHousekeeperMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic())); - } - - @PreDestroy - private void destroy() { - if (coreAdmin != null) { - coreAdmin.destroy(); - } - if (transportApiAdmin != null) { - transportApiAdmin.destroy(); - } - if (notificationAdmin != null) { - notificationAdmin.destroy(); - } - if (ruleEngineAdmin != null) { - ruleEngineAdmin.destroy(); - } - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java deleted file mode 100644 index 3df48e8fb8..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java +++ /dev/null @@ -1,309 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.provider; - -import com.google.protobuf.util.JsonFormat; -import jakarta.annotation.PreDestroy; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.context.annotation.Bean; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.queue.Queue; -import org.thingsboard.server.common.msg.queue.ServiceType; -import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsRequest; -import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsResponse; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToEdgeMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToEdgeNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToHousekeeperServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToVersionControlServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueConsumer; -import org.thingsboard.server.queue.TbQueueProducer; -import org.thingsboard.server.queue.TbQueueRequestTemplate; -import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; -import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; -import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; -import org.thingsboard.server.queue.discovery.TopicService; -import org.thingsboard.server.queue.pubsub.TbPubSubAdmin; -import org.thingsboard.server.queue.pubsub.TbPubSubConsumerTemplate; -import org.thingsboard.server.queue.pubsub.TbPubSubProducerTemplate; -import org.thingsboard.server.queue.pubsub.TbPubSubSettings; -import org.thingsboard.server.queue.pubsub.TbPubSubSubscriptionSettings; -import org.thingsboard.server.queue.settings.TbQueueCoreSettings; -import org.thingsboard.server.queue.settings.TbQueueEdgeSettings; -import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings; -import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; -import org.thingsboard.server.queue.settings.TbQueueVersionControlSettings; - -import java.nio.charset.StandardCharsets; - -@Component -@ConditionalOnExpression("'${queue.type:null}'=='pubsub' && '${service.type:null}'=='monolith'") -public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngineQueueFactory, TbVersionControlQueueFactory { - - private final TbPubSubSettings pubSubSettings; - private final TbQueueCoreSettings coreSettings; - private final TbQueueRuleEngineSettings ruleEngineSettings; - private final TbQueueTransportApiSettings transportApiSettings; - private final TbQueueTransportNotificationSettings transportNotificationSettings; - private final TopicService topicService; - private final TbServiceInfoProvider serviceInfoProvider; - private final TbQueueRemoteJsInvokeSettings jsInvokeSettings; - private final TbQueueVersionControlSettings vcSettings; - private final TbQueueEdgeSettings edgeSettings; - - private final TbQueueAdmin coreAdmin; - private final TbQueueAdmin ruleEngineAdmin; - private final TbQueueAdmin jsExecutorAdmin; - private final TbQueueAdmin transportApiAdmin; - private final TbQueueAdmin notificationAdmin; - private final TbQueueAdmin vcAdmin; - private final TbQueueAdmin edgeAdmin; - - public PubSubMonolithQueueFactory(TbPubSubSettings pubSubSettings, - TbQueueCoreSettings coreSettings, - TbQueueRuleEngineSettings ruleEngineSettings, - TbQueueTransportApiSettings transportApiSettings, - TbQueueTransportNotificationSettings transportNotificationSettings, - TopicService topicService, - TbServiceInfoProvider serviceInfoProvider, - TbPubSubSubscriptionSettings pubSubSubscriptionSettings, - TbQueueRemoteJsInvokeSettings jsInvokeSettings, - TbQueueVersionControlSettings vcSettings, - TbQueueEdgeSettings edgeSettings) { - this.pubSubSettings = pubSubSettings; - this.coreSettings = coreSettings; - this.ruleEngineSettings = ruleEngineSettings; - this.transportApiSettings = transportApiSettings; - this.transportNotificationSettings = transportNotificationSettings; - this.topicService = topicService; - this.serviceInfoProvider = serviceInfoProvider; - this.vcSettings = vcSettings; - this.edgeSettings = edgeSettings; - - this.coreAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getCoreSettings()); - this.ruleEngineAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getRuleEngineSettings()); - this.jsExecutorAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getJsExecutorSettings()); - this.transportApiAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getTransportApiSettings()); - this.notificationAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getNotificationsSettings()); - this.vcAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getVcSettings()); - this.edgeAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getEdgeSettings()); - - this.jsInvokeSettings = jsInvokeSettings; - } - - @Override - public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); - } - - @Override - public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbPubSubProducerTemplate<>(ruleEngineAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); - - } - - @Override - public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueConsumer> createToVersionControlMsgConsumer() { - return new TbPubSubConsumerTemplate<>(vcAdmin, pubSubSettings, topicService.buildTopicName(vcSettings.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders()) - ); - } - - @Override - public TbQueueConsumer> createToRuleEngineMsgConsumer(Queue configuration) { - return new TbPubSubConsumerTemplate<>(ruleEngineAdmin, pubSubSettings, topicService.buildTopicName(configuration.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToRuleEngineNotificationsMsgConsumer() { - return new TbPubSubConsumerTemplate<>(notificationAdmin, pubSubSettings, - topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToCoreMsgConsumer() { - return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToCoreNotificationsMsgConsumer() { - return new TbPubSubConsumerTemplate<>(notificationAdmin, pubSubSettings, - topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createTransportApiRequestConsumer() { - return new TbPubSubConsumerTemplate<>(transportApiAdmin, pubSubSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createTransportApiResponseProducer() { - return new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, topicService.buildTopicName(transportApiSettings.getResponsesTopic())); - } - - @Override - @Bean - public TbQueueRequestTemplate, TbProtoQueueMsg> createRemoteJsRequestTemplate() { - TbQueueProducer> producer = new TbPubSubProducerTemplate<>(jsExecutorAdmin, pubSubSettings, jsInvokeSettings.getRequestTopic()); - TbQueueConsumer> consumer = new TbPubSubConsumerTemplate<>(jsExecutorAdmin, pubSubSettings, - jsInvokeSettings.getResponseTopic() + "." + serviceInfoProvider.getServiceId(), - msg -> { - RemoteJsResponse.Builder builder = RemoteJsResponse.newBuilder(); - JsonFormat.parser().ignoringUnknownFields().merge(new String(msg.getData(), StandardCharsets.UTF_8), builder); - return new TbProtoQueueMsg<>(msg.getKey(), builder.build(), msg.getHeaders()); - }); - - DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder - , TbProtoQueueMsg> builder = DefaultTbQueueRequestTemplate.builder(); - builder.queueAdmin(jsExecutorAdmin); - builder.requestTemplate(producer); - builder.responseTemplate(consumer); - builder.maxPendingRequests(jsInvokeSettings.getMaxPendingRequests()); - builder.maxRequestTimeout(jsInvokeSettings.getMaxRequestsTimeout()); - builder.pollInterval(jsInvokeSettings.getResponsePollInterval()); - return builder.build(); - } - - @Override - public TbQueueConsumer> createToUsageStatsServiceMsgConsumer() { - return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { - return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); - } - - @Override - public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); - } - - @Override - public TbQueueProducer> createVersionControlMsgProducer() { - return new TbPubSubProducerTemplate<>(vcAdmin, pubSubSettings, topicService.buildTopicName(vcSettings.getTopic())); - } - - @Override - public TbQueueProducer> createHousekeeperMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic())); - } - - @Override - public TbQueueConsumer> createHousekeeperMsgConsumer() { - return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToHousekeeperServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createHousekeeperReprocessingMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getHousekeeperReprocessingTopic())); - } - - @Override - public TbQueueConsumer> createHousekeeperReprocessingMsgConsumer() { - return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getHousekeeperReprocessingTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToHousekeeperServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createEdgeMsgProducer() { - return new TbPubSubProducerTemplate<>(edgeAdmin, pubSubSettings, topicService.buildTopicName(edgeSettings.getTopic())); - } - - @Override - public TbQueueConsumer> createEdgeMsgConsumer() { - return new TbPubSubConsumerTemplate<>(edgeAdmin, pubSubSettings, topicService.buildTopicName(edgeSettings.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToEdgeMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToEdgeNotificationsMsgConsumer() { - return new TbPubSubConsumerTemplate<>(notificationAdmin, pubSubSettings, - topicService.getEdgeNotificationsTopic(serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToEdgeNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createEdgeNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, - topicService.getEdgeNotificationsTopic(serviceInfoProvider.getServiceId()).getFullTopicName()); - } - - @PreDestroy - private void destroy() { - if (coreAdmin != null) { - coreAdmin.destroy(); - } - if (ruleEngineAdmin != null) { - ruleEngineAdmin.destroy(); - } - if (jsExecutorAdmin != null) { - jsExecutorAdmin.destroy(); - } - if (transportApiAdmin != null) { - transportApiAdmin.destroy(); - } - if (notificationAdmin != null) { - notificationAdmin.destroy(); - } - if (vcAdmin != null) { - vcAdmin.destroy(); - } - if (edgeAdmin != null) { - edgeAdmin.destroy(); - } - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java deleted file mode 100644 index 919d895a97..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java +++ /dev/null @@ -1,278 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.provider; - -import com.google.protobuf.util.JsonFormat; -import jakarta.annotation.PreDestroy; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.context.annotation.Bean; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.msg.queue.ServiceType; -import org.thingsboard.server.gen.js.JsInvokeProtos; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToEdgeMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToEdgeNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToHousekeeperServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToVersionControlServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueConsumer; -import org.thingsboard.server.queue.TbQueueProducer; -import org.thingsboard.server.queue.TbQueueRequestTemplate; -import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; -import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; -import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; -import org.thingsboard.server.queue.discovery.TopicService; -import org.thingsboard.server.queue.pubsub.TbPubSubAdmin; -import org.thingsboard.server.queue.pubsub.TbPubSubConsumerTemplate; -import org.thingsboard.server.queue.pubsub.TbPubSubProducerTemplate; -import org.thingsboard.server.queue.pubsub.TbPubSubSettings; -import org.thingsboard.server.queue.pubsub.TbPubSubSubscriptionSettings; -import org.thingsboard.server.queue.settings.TbQueueCoreSettings; -import org.thingsboard.server.queue.settings.TbQueueEdgeSettings; -import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings; -import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; - -import java.nio.charset.StandardCharsets; - -@Component -@ConditionalOnExpression("'${queue.type:null}'=='pubsub' && '${service.type:null}'=='tb-core'") -public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory { - - private final TbPubSubSettings pubSubSettings; - private final TbQueueCoreSettings coreSettings; - private final TbQueueTransportApiSettings transportApiSettings; - private final TopicService topicService; - private final TbServiceInfoProvider serviceInfoProvider; - private final TbQueueRemoteJsInvokeSettings jsInvokeSettings; - private final TbQueueTransportNotificationSettings transportNotificationSettings; - private final TbQueueRuleEngineSettings ruleEngineSettings; - private final TbQueueEdgeSettings edgeSettings; - - private final TbQueueAdmin coreAdmin; - private final TbQueueAdmin jsExecutorAdmin; - private final TbQueueAdmin transportApiAdmin; - private final TbQueueAdmin notificationAdmin; - private final TbQueueAdmin ruleEngineAdmin; - private final TbQueueAdmin edgeAdmin; - - public PubSubTbCoreQueueFactory(TbPubSubSettings pubSubSettings, - TbQueueCoreSettings coreSettings, - TbQueueTransportApiSettings transportApiSettings, - TopicService topicService, - TbServiceInfoProvider serviceInfoProvider, - TbQueueRemoteJsInvokeSettings jsInvokeSettings, - TbQueueTransportNotificationSettings transportNotificationSettings, - TbQueueRuleEngineSettings ruleEngineSettings, - TbQueueEdgeSettings edgeSettings, - TbPubSubSubscriptionSettings pubSubSubscriptionSettings) { - this.pubSubSettings = pubSubSettings; - this.coreSettings = coreSettings; - this.transportApiSettings = transportApiSettings; - this.topicService = topicService; - this.serviceInfoProvider = serviceInfoProvider; - this.jsInvokeSettings = jsInvokeSettings; - this.transportNotificationSettings = transportNotificationSettings; - this.ruleEngineSettings = ruleEngineSettings; - this.edgeSettings = edgeSettings; - - this.coreAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getCoreSettings()); - this.jsExecutorAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getJsExecutorSettings()); - this.transportApiAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getTransportApiSettings()); - this.notificationAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getNotificationsSettings()); - this.ruleEngineAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getRuleEngineSettings()); - this.edgeAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getEdgeSettings()); - } - - @Override - public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); - } - - @Override - public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, - topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName()); - } - - @Override - public TbQueueConsumer> createToCoreMsgConsumer() { - return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToCoreNotificationsMsgConsumer() { - return new TbPubSubConsumerTemplate<>(notificationAdmin, pubSubSettings, - topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createTransportApiRequestConsumer() { - return new TbPubSubConsumerTemplate<>(transportApiAdmin, pubSubSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createTransportApiResponseProducer() { - return new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, topicService.buildTopicName(transportApiSettings.getResponsesTopic())); - } - - @Override - @Bean - public TbQueueRequestTemplate, TbProtoQueueMsg> createRemoteJsRequestTemplate() { - TbQueueProducer> producer = new TbPubSubProducerTemplate<>(jsExecutorAdmin, pubSubSettings, jsInvokeSettings.getRequestTopic()); - TbQueueConsumer> consumer = new TbPubSubConsumerTemplate<>(jsExecutorAdmin, pubSubSettings, - jsInvokeSettings.getResponseTopic() + "." + serviceInfoProvider.getServiceId(), - msg -> { - JsInvokeProtos.RemoteJsResponse.Builder builder = JsInvokeProtos.RemoteJsResponse.newBuilder(); - JsonFormat.parser().ignoringUnknownFields().merge(new String(msg.getData(), StandardCharsets.UTF_8), builder); - return new TbProtoQueueMsg<>(msg.getKey(), builder.build(), msg.getHeaders()); - }); - - DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder - , TbProtoQueueMsg> builder = DefaultTbQueueRequestTemplate.builder(); - builder.queueAdmin(jsExecutorAdmin); - builder.requestTemplate(producer); - builder.responseTemplate(consumer); - builder.maxPendingRequests(jsInvokeSettings.getMaxPendingRequests()); - builder.maxRequestTimeout(jsInvokeSettings.getMaxRequestsTimeout()); - builder.pollInterval(jsInvokeSettings.getResponsePollInterval()); - return builder.build(); - } - - @Override - public TbQueueConsumer> createToUsageStatsServiceMsgConsumer() { - return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { - return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); - } - - @Override - public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); - } - - @Override - public TbQueueProducer> createVersionControlMsgProducer() { - //TODO: version-control - return null; - } - - @Override - public TbQueueProducer> createHousekeeperMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic())); - } - - @Override - public TbQueueConsumer> createHousekeeperMsgConsumer() { - return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToHousekeeperServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createHousekeeperReprocessingMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getHousekeeperReprocessingTopic())); - } - - @Override - public TbQueueConsumer> createHousekeeperReprocessingMsgConsumer() { - return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getHousekeeperReprocessingTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToHousekeeperServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createEdgeMsgConsumer() { - return new TbPubSubConsumerTemplate<>(edgeAdmin, pubSubSettings, topicService.buildTopicName(edgeSettings.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToEdgeMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createEdgeMsgProducer() { - return new TbPubSubProducerTemplate<>(edgeAdmin, pubSubSettings, topicService.buildTopicName(edgeSettings.getTopic())); - } - - @Override - public TbQueueConsumer> createToEdgeNotificationsMsgConsumer() { - return new TbPubSubConsumerTemplate<>(notificationAdmin, pubSubSettings, - topicService.getEdgeNotificationsTopic(serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToEdgeNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createEdgeNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, - topicService.getEdgeNotificationsTopic(serviceInfoProvider.getServiceId()).getFullTopicName()); - } - - @PreDestroy - private void destroy() { - if (coreAdmin != null) { - coreAdmin.destroy(); - } - if (jsExecutorAdmin != null) { - jsExecutorAdmin.destroy(); - } - if (transportApiAdmin != null) { - transportApiAdmin.destroy(); - } - if (notificationAdmin != null) { - notificationAdmin.destroy(); - } - if (ruleEngineAdmin != null) { - ruleEngineAdmin.destroy(); - } - if (edgeAdmin != null) { - edgeAdmin.destroy(); - } - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbRuleEngineQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbRuleEngineQueueFactory.java deleted file mode 100644 index bb46610de5..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbRuleEngineQueueFactory.java +++ /dev/null @@ -1,203 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.provider; - -import com.google.protobuf.util.JsonFormat; -import jakarta.annotation.PreDestroy; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.context.annotation.Bean; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.queue.Queue; -import org.thingsboard.server.common.msg.queue.ServiceType; -import org.thingsboard.server.gen.js.JsInvokeProtos; -import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToEdgeMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToHousekeeperServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueConsumer; -import org.thingsboard.server.queue.TbQueueProducer; -import org.thingsboard.server.queue.TbQueueRequestTemplate; -import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; -import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; -import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; -import org.thingsboard.server.queue.discovery.TopicService; -import org.thingsboard.server.queue.pubsub.TbPubSubAdmin; -import org.thingsboard.server.queue.pubsub.TbPubSubConsumerTemplate; -import org.thingsboard.server.queue.pubsub.TbPubSubProducerTemplate; -import org.thingsboard.server.queue.pubsub.TbPubSubSettings; -import org.thingsboard.server.queue.pubsub.TbPubSubSubscriptionSettings; -import org.thingsboard.server.queue.settings.TbQueueCoreSettings; -import org.thingsboard.server.queue.settings.TbQueueEdgeSettings; -import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings; -import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; - -import java.nio.charset.StandardCharsets; - -@Component -@ConditionalOnExpression("'${queue.type:null}'=='pubsub' && '${service.type:null}'=='tb-rule-engine'") -public class PubSubTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { - - private final TbPubSubSettings pubSubSettings; - private final TbQueueCoreSettings coreSettings; - private final TbQueueRuleEngineSettings ruleEngineSettings; - private final TopicService topicService; - private final TbServiceInfoProvider serviceInfoProvider; - private final TbQueueRemoteJsInvokeSettings jsInvokeSettings; - private final TbQueueTransportNotificationSettings transportNotificationSettings; - private final TbQueueEdgeSettings edgeSettings; - - private final TbQueueAdmin coreAdmin; - private final TbQueueAdmin ruleEngineAdmin; - private final TbQueueAdmin jsExecutorAdmin; - private final TbQueueAdmin notificationAdmin; - private final TbQueueAdmin edgeAdmin; - - public PubSubTbRuleEngineQueueFactory(TbPubSubSettings pubSubSettings, - TbQueueCoreSettings coreSettings, - TbQueueRuleEngineSettings ruleEngineSettings, - TopicService topicService, - TbServiceInfoProvider serviceInfoProvider, - TbQueueRemoteJsInvokeSettings jsInvokeSettings, - TbQueueTransportNotificationSettings transportNotificationSettings, - TbPubSubSubscriptionSettings pubSubSubscriptionSettings, - TbQueueEdgeSettings edgeSettings) { - this.pubSubSettings = pubSubSettings; - this.coreSettings = coreSettings; - this.ruleEngineSettings = ruleEngineSettings; - this.topicService = topicService; - this.serviceInfoProvider = serviceInfoProvider; - this.jsInvokeSettings = jsInvokeSettings; - this.transportNotificationSettings = transportNotificationSettings; - this.edgeSettings = edgeSettings; - - this.coreAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getCoreSettings()); - this.ruleEngineAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getRuleEngineSettings()); - this.jsExecutorAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getJsExecutorSettings()); - this.notificationAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getNotificationsSettings()); - this.edgeAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getEdgeSettings()); - } - - @Override - public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); - } - - @Override - public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbPubSubProducerTemplate<>(ruleEngineAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); - } - - @Override - public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueProducer> createEdgeMsgProducer() { - return new TbPubSubProducerTemplate<>(edgeAdmin, pubSubSettings, topicService.buildTopicName(edgeSettings.getTopic())); - } - - @Override - public TbQueueProducer> createEdgeNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.getEdgeNotificationsTopic(serviceInfoProvider.getServiceId()).getFullTopicName()); - } - - @Override - public TbQueueConsumer> createToRuleEngineMsgConsumer(Queue configuration) { - return new TbPubSubConsumerTemplate<>(ruleEngineAdmin, pubSubSettings, topicService.buildTopicName(configuration.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToRuleEngineNotificationsMsgConsumer() { - return new TbPubSubConsumerTemplate<>(notificationAdmin, pubSubSettings, - topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - @Bean - public TbQueueRequestTemplate, TbProtoQueueMsg> createRemoteJsRequestTemplate() { - TbQueueProducer> producer = new TbPubSubProducerTemplate<>(jsExecutorAdmin, pubSubSettings, jsInvokeSettings.getRequestTopic()); - TbQueueConsumer> consumer = new TbPubSubConsumerTemplate<>(jsExecutorAdmin, pubSubSettings, - jsInvokeSettings.getResponseTopic() + "." + serviceInfoProvider.getServiceId(), - msg -> { - JsInvokeProtos.RemoteJsResponse.Builder builder = JsInvokeProtos.RemoteJsResponse.newBuilder(); - JsonFormat.parser().ignoringUnknownFields().merge(new String(msg.getData(), StandardCharsets.UTF_8), builder); - return new TbProtoQueueMsg<>(msg.getKey(), builder.build(), msg.getHeaders()); - }); - - DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder - , TbProtoQueueMsg> builder = DefaultTbQueueRequestTemplate.builder(); - builder.queueAdmin(jsExecutorAdmin); - builder.requestTemplate(producer); - builder.responseTemplate(consumer); - builder.maxPendingRequests(jsInvokeSettings.getMaxPendingRequests()); - builder.maxRequestTimeout(jsInvokeSettings.getMaxRequestsTimeout()); - builder.pollInterval(jsInvokeSettings.getResponsePollInterval()); - return builder.build(); - } - - @Override - public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); - } - - @Override - public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); - } - - @Override - public TbQueueProducer> createHousekeeperMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic())); - } - - @PreDestroy - private void destroy() { - if (coreAdmin != null) { - coreAdmin.destroy(); - } - if (ruleEngineAdmin != null) { - ruleEngineAdmin.destroy(); - } - if (jsExecutorAdmin != null) { - jsExecutorAdmin.destroy(); - } - if (notificationAdmin != null) { - notificationAdmin.destroy(); - } - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbVersionControlQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbVersionControlQueueFactory.java deleted file mode 100644 index b6da4e2973..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbVersionControlQueueFactory.java +++ /dev/null @@ -1,98 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.provider; - -import jakarta.annotation.PreDestroy; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.stereotype.Component; -import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueConsumer; -import org.thingsboard.server.queue.TbQueueProducer; -import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.TopicService; -import org.thingsboard.server.queue.pubsub.TbPubSubAdmin; -import org.thingsboard.server.queue.pubsub.TbPubSubConsumerTemplate; -import org.thingsboard.server.queue.pubsub.TbPubSubProducerTemplate; -import org.thingsboard.server.queue.pubsub.TbPubSubSettings; -import org.thingsboard.server.queue.pubsub.TbPubSubSubscriptionSettings; -import org.thingsboard.server.queue.settings.TbQueueCoreSettings; -import org.thingsboard.server.queue.settings.TbQueueVersionControlSettings; - -@Component -@ConditionalOnExpression("'${queue.type:null}'=='pubsub' && '${service.type:null}'=='tb-vc-executor'") -public class PubSubTbVersionControlQueueFactory implements TbVersionControlQueueFactory { - - private final TbPubSubSettings pubSubSettings; - private final TbQueueCoreSettings coreSettings; - private final TbQueueVersionControlSettings vcSettings; - private final TopicService topicService; - - private final TbQueueAdmin coreAdmin; - private final TbQueueAdmin notificationAdmin; - private final TbQueueAdmin vcAdmin; - - public PubSubTbVersionControlQueueFactory(TbPubSubSettings pubSubSettings, - TbQueueCoreSettings coreSettings, - TbQueueVersionControlSettings vcSettings, - TbPubSubSubscriptionSettings pubSubSubscriptionSettings, - TopicService topicService - ) { - this.pubSubSettings = pubSubSettings; - this.coreSettings = coreSettings; - this.vcSettings = vcSettings; - this.topicService = topicService; - - this.coreAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getCoreSettings()); - this.notificationAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getNotificationsSettings()); - this.vcAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getVcSettings()); - } - - @Override - public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); - } - - @Override - public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueConsumer> createToVersionControlMsgConsumer() { - return new TbPubSubConsumerTemplate<>(vcAdmin, pubSubSettings, topicService.buildTopicName(vcSettings.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders()) - ); - } - - @Override - public TbQueueProducer> createHousekeeperMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic())); - } - - @PreDestroy - private void destroy() { - if (coreAdmin != null) { - coreAdmin.destroy(); - } - if (notificationAdmin != null) { - notificationAdmin.destroy(); - } - if (vcAdmin != null) { - vcAdmin.destroy(); - } - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTransportQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTransportQueueFactory.java deleted file mode 100644 index a7500d2083..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTransportQueueFactory.java +++ /dev/null @@ -1,153 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.provider; - -import jakarta.annotation.PreDestroy; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.stereotype.Component; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToHousekeeperServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueConsumer; -import org.thingsboard.server.queue.TbQueueProducer; -import org.thingsboard.server.queue.TbQueueRequestTemplate; -import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; -import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; -import org.thingsboard.server.queue.discovery.TopicService; -import org.thingsboard.server.queue.pubsub.TbPubSubAdmin; -import org.thingsboard.server.queue.pubsub.TbPubSubConsumerTemplate; -import org.thingsboard.server.queue.pubsub.TbPubSubProducerTemplate; -import org.thingsboard.server.queue.pubsub.TbPubSubSettings; -import org.thingsboard.server.queue.pubsub.TbPubSubSubscriptionSettings; -import org.thingsboard.server.queue.settings.TbQueueCoreSettings; -import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; - -@Component -@ConditionalOnExpression("'${queue.type:null}'=='pubsub' && (('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true') || '${service.type:null}'=='tb-transport')") -@Slf4j -public class PubSubTransportQueueFactory implements TbTransportQueueFactory { - - private final TbPubSubSettings pubSubSettings; - private final TbServiceInfoProvider serviceInfoProvider; - private final TbQueueCoreSettings coreSettings; - private final TbQueueRuleEngineSettings ruleEngineSettings; - private final TbQueueTransportApiSettings transportApiSettings; - private final TbQueueTransportNotificationSettings transportNotificationSettings; - private final TopicService topicService; - - private final TbQueueAdmin coreAdmin; - private final TbQueueAdmin ruleEngineAdmin; - private final TbQueueAdmin transportApiAdmin; - private final TbQueueAdmin notificationAdmin; - - public PubSubTransportQueueFactory(TbPubSubSettings pubSubSettings, - TbServiceInfoProvider serviceInfoProvider, - TbQueueCoreSettings coreSettings, - TbQueueRuleEngineSettings ruleEngineSettings, - TbQueueTransportApiSettings transportApiSettings, - TbQueueTransportNotificationSettings transportNotificationSettings, - TbPubSubSubscriptionSettings pubSubSubscriptionSettings, - TopicService topicService) { - this.pubSubSettings = pubSubSettings; - this.serviceInfoProvider = serviceInfoProvider; - this.coreSettings = coreSettings; - this.ruleEngineSettings = ruleEngineSettings; - this.transportApiSettings = transportApiSettings; - this.transportNotificationSettings = transportNotificationSettings; - this.topicService = topicService; - - this.coreAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getCoreSettings()); - this.ruleEngineAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getRuleEngineSettings()); - this.transportApiAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getTransportApiSettings()); - this.notificationAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getNotificationsSettings()); - } - - @Override - public TbQueueRequestTemplate, TbProtoQueueMsg> createTransportApiRequestTemplate() { - TbQueueProducer> producer = new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic())); - TbQueueConsumer> consumer = new TbPubSubConsumerTemplate<>(transportApiAdmin, pubSubSettings, - topicService.buildTopicName(transportApiSettings.getResponsesTopic() + "." + serviceInfoProvider.getServiceId()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiResponseMsg.parseFrom(msg.getData()), msg.getHeaders())); - - DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder - , TbProtoQueueMsg> templateBuilder = DefaultTbQueueRequestTemplate.builder(); - templateBuilder.queueAdmin(transportApiAdmin); - templateBuilder.requestTemplate(producer); - templateBuilder.responseTemplate(consumer); - templateBuilder.maxPendingRequests(transportApiSettings.getMaxPendingRequests()); - templateBuilder.maxRequestTimeout(transportApiSettings.getMaxRequestsTimeout()); - templateBuilder.pollInterval(transportApiSettings.getResponsePollInterval()); - return templateBuilder.build(); - } - - @Override - public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbPubSubProducerTemplate<>(ruleEngineAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueConsumer> createTransportNotificationsConsumer() { - return new TbPubSubConsumerTemplate<>(notificationAdmin, pubSubSettings, - topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic() + "." + serviceInfoProvider.getServiceId()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToTransportMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); - } - - @Override - public TbQueueProducer> createHousekeeperMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic())); - } - - @PreDestroy - private void destroy() { - if (coreAdmin != null) { - coreAdmin.destroy(); - } - if (ruleEngineAdmin != null) { - ruleEngineAdmin.destroy(); - } - if (transportApiAdmin != null) { - transportApiAdmin.destroy(); - } - if (notificationAdmin != null) { - notificationAdmin.destroy(); - } - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqMonolithQueueFactory.java deleted file mode 100644 index 8ad400d9e3..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqMonolithQueueFactory.java +++ /dev/null @@ -1,307 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.provider; - -import com.google.protobuf.util.JsonFormat; -import jakarta.annotation.PreDestroy; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.context.annotation.Bean; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.queue.Queue; -import org.thingsboard.server.common.msg.queue.ServiceType; -import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsRequest; -import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsResponse; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToEdgeMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToEdgeNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToHousekeeperServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToVersionControlServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueConsumer; -import org.thingsboard.server.queue.TbQueueProducer; -import org.thingsboard.server.queue.TbQueueRequestTemplate; -import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; -import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; -import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; -import org.thingsboard.server.queue.discovery.TopicService; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqAdmin; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqConsumerTemplate; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqProducerTemplate; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqQueueArguments; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqSettings; -import org.thingsboard.server.queue.settings.TbQueueCoreSettings; -import org.thingsboard.server.queue.settings.TbQueueEdgeSettings; -import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings; -import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; -import org.thingsboard.server.queue.settings.TbQueueVersionControlSettings; - -import java.nio.charset.StandardCharsets; - -@Component -@ConditionalOnExpression("'${queue.type:null}'=='rabbitmq' && '${service.type:null}'=='monolith'") -public class RabbitMqMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngineQueueFactory, TbVersionControlQueueFactory { - - private final TopicService topicService; - private final TbQueueCoreSettings coreSettings; - private final TbServiceInfoProvider serviceInfoProvider; - private final TbQueueRuleEngineSettings ruleEngineSettings; - private final TbQueueTransportApiSettings transportApiSettings; - private final TbQueueTransportNotificationSettings transportNotificationSettings; - private final TbRabbitMqSettings rabbitMqSettings; - private final TbQueueRemoteJsInvokeSettings jsInvokeSettings; - private final TbQueueVersionControlSettings vcSettings; - private final TbQueueEdgeSettings edgeSettings; - - private final TbQueueAdmin coreAdmin; - private final TbQueueAdmin ruleEngineAdmin; - private final TbQueueAdmin jsExecutorAdmin; - private final TbQueueAdmin transportApiAdmin; - private final TbQueueAdmin notificationAdmin; - private final TbQueueAdmin vcAdmin; - private final TbQueueAdmin edgeAdmin; - - public RabbitMqMonolithQueueFactory(TopicService topicService, TbQueueCoreSettings coreSettings, - TbQueueRuleEngineSettings ruleEngineSettings, - TbServiceInfoProvider serviceInfoProvider, - TbQueueTransportApiSettings transportApiSettings, - TbQueueTransportNotificationSettings transportNotificationSettings, - TbRabbitMqSettings rabbitMqSettings, - TbRabbitMqQueueArguments queueArguments, - TbQueueRemoteJsInvokeSettings jsInvokeSettings, - TbQueueVersionControlSettings vcSettings, - TbQueueEdgeSettings edgeSettings) { - this.topicService = topicService; - this.coreSettings = coreSettings; - this.serviceInfoProvider = serviceInfoProvider; - this.ruleEngineSettings = ruleEngineSettings; - this.transportApiSettings = transportApiSettings; - this.transportNotificationSettings = transportNotificationSettings; - this.rabbitMqSettings = rabbitMqSettings; - this.jsInvokeSettings = jsInvokeSettings; - this.vcSettings = vcSettings; - this.edgeSettings = edgeSettings; - - this.coreAdmin = new TbRabbitMqAdmin(rabbitMqSettings, queueArguments.getCoreArgs()); - this.ruleEngineAdmin = new TbRabbitMqAdmin(rabbitMqSettings, queueArguments.getRuleEngineArgs()); - this.jsExecutorAdmin = new TbRabbitMqAdmin(rabbitMqSettings, queueArguments.getJsExecutorArgs()); - this.transportApiAdmin = new TbRabbitMqAdmin(rabbitMqSettings, queueArguments.getTransportApiArgs()); - this.notificationAdmin = new TbRabbitMqAdmin(rabbitMqSettings, queueArguments.getNotificationsArgs()); - this.vcAdmin = new TbRabbitMqAdmin(rabbitMqSettings, queueArguments.getVcArgs()); - this.edgeAdmin = new TbRabbitMqAdmin(rabbitMqSettings, queueArguments.getEdgeArgs()); - } - - @Override - public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); - } - - @Override - public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbRabbitMqProducerTemplate<>(ruleEngineAdmin, rabbitMqSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); - } - - @Override - public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, - topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName()); - } - - @Override - public TbQueueConsumer> createToVersionControlMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(vcAdmin, rabbitMqSettings, topicService.buildTopicName(vcSettings.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders()) - ); - } - - @Override - public TbQueueConsumer> createToRuleEngineMsgConsumer(Queue configuration) { - return new TbRabbitMqConsumerTemplate<>(ruleEngineAdmin, rabbitMqSettings, topicService.buildTopicName(configuration.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToRuleEngineNotificationsMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(notificationAdmin, rabbitMqSettings, - topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToCoreMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToCoreNotificationsMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(notificationAdmin, rabbitMqSettings, - topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createTransportApiRequestConsumer() { - return new TbRabbitMqConsumerTemplate<>(transportApiAdmin, rabbitMqSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createTransportApiResponseProducer() { - return new TbRabbitMqProducerTemplate<>(transportApiAdmin, rabbitMqSettings, topicService.buildTopicName(transportApiSettings.getResponsesTopic())); - } - - @Override - @Bean - public TbQueueRequestTemplate, TbProtoQueueMsg> createRemoteJsRequestTemplate() { - TbQueueProducer> producer = new TbRabbitMqProducerTemplate<>(jsExecutorAdmin, rabbitMqSettings, jsInvokeSettings.getRequestTopic()); - TbQueueConsumer> consumer = new TbRabbitMqConsumerTemplate<>(jsExecutorAdmin, rabbitMqSettings, - jsInvokeSettings.getResponseTopic() + "." + serviceInfoProvider.getServiceId(), - msg -> { - RemoteJsResponse.Builder builder = RemoteJsResponse.newBuilder(); - JsonFormat.parser().ignoringUnknownFields().merge(new String(msg.getData(), StandardCharsets.UTF_8), builder); - return new TbProtoQueueMsg<>(msg.getKey(), builder.build(), msg.getHeaders()); - }); - - DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder - , TbProtoQueueMsg> builder = DefaultTbQueueRequestTemplate.builder(); - builder.queueAdmin(jsExecutorAdmin); - builder.requestTemplate(producer); - builder.responseTemplate(consumer); - builder.maxPendingRequests(jsInvokeSettings.getMaxPendingRequests()); - builder.maxRequestTimeout(jsInvokeSettings.getMaxRequestsTimeout()); - builder.pollInterval(jsInvokeSettings.getResponsePollInterval()); - return builder.build(); - } - - @Override - public TbQueueConsumer> createToUsageStatsServiceMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); - } - - @Override - public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); - } - - @Override - public TbQueueProducer> createVersionControlMsgProducer() { - return new TbRabbitMqProducerTemplate<>(vcAdmin, rabbitMqSettings, topicService.buildTopicName(vcSettings.getTopic())); - } - - @Override - public TbQueueProducer> createHousekeeperMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic())); - } - - @Override - public TbQueueConsumer> createHousekeeperMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToHousekeeperServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createHousekeeperReprocessingMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getHousekeeperReprocessingTopic())); - } - - @Override - public TbQueueConsumer> createHousekeeperReprocessingMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getHousekeeperReprocessingTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToHousekeeperServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createEdgeMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(edgeAdmin, rabbitMqSettings, topicService.buildTopicName(edgeSettings.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToEdgeMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createEdgeMsgProducer() { - return new TbRabbitMqProducerTemplate<>(edgeAdmin, rabbitMqSettings, topicService.buildTopicName(edgeSettings.getTopic())); - } - - @Override - public TbQueueConsumer> createToEdgeNotificationsMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(notificationAdmin, rabbitMqSettings, - topicService.getEdgeNotificationsTopic(serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToEdgeNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createEdgeNotificationsMsgProducer() { - return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, - topicService.getEdgeNotificationsTopic(serviceInfoProvider.getServiceId()).getFullTopicName()); - } - - @PreDestroy - private void destroy() { - if (coreAdmin != null) { - coreAdmin.destroy(); - } - if (ruleEngineAdmin != null) { - ruleEngineAdmin.destroy(); - } - if (jsExecutorAdmin != null) { - jsExecutorAdmin.destroy(); - } - if (transportApiAdmin != null) { - transportApiAdmin.destroy(); - } - if (notificationAdmin != null) { - notificationAdmin.destroy(); - } - if (vcAdmin != null) { - vcAdmin.destroy(); - } - if (edgeAdmin != null) { - edgeAdmin.destroy(); - } - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbCoreQueueFactory.java deleted file mode 100644 index 16f9838384..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbCoreQueueFactory.java +++ /dev/null @@ -1,278 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.provider; - -import com.google.protobuf.util.JsonFormat; -import jakarta.annotation.PreDestroy; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.context.annotation.Bean; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.msg.queue.ServiceType; -import org.thingsboard.server.gen.js.JsInvokeProtos; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToEdgeMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToEdgeNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToHousekeeperServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToVersionControlServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueConsumer; -import org.thingsboard.server.queue.TbQueueProducer; -import org.thingsboard.server.queue.TbQueueRequestTemplate; -import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; -import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; -import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; -import org.thingsboard.server.queue.discovery.TopicService; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqAdmin; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqConsumerTemplate; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqProducerTemplate; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqQueueArguments; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqSettings; -import org.thingsboard.server.queue.settings.TbQueueCoreSettings; -import org.thingsboard.server.queue.settings.TbQueueEdgeSettings; -import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings; -import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; - -import java.nio.charset.StandardCharsets; - -@Component -@ConditionalOnExpression("'${queue.type:null}'=='rabbitmq' && '${service.type:null}'=='tb-core'") -public class RabbitMqTbCoreQueueFactory implements TbCoreQueueFactory { - - private final TbRabbitMqSettings rabbitMqSettings; - private final TbQueueRuleEngineSettings ruleEngineSettings; - private final TbQueueCoreSettings coreSettings; - private final TbQueueTransportApiSettings transportApiSettings; - private final TopicService topicService; - private final TbServiceInfoProvider serviceInfoProvider; - private final TbQueueRemoteJsInvokeSettings jsInvokeSettings; - private final TbQueueTransportNotificationSettings transportNotificationSettings; - private final TbQueueEdgeSettings edgeSettings; - - private final TbQueueAdmin coreAdmin; - private final TbQueueAdmin ruleEngineAdmin; - private final TbQueueAdmin jsExecutorAdmin; - private final TbQueueAdmin transportApiAdmin; - private final TbQueueAdmin notificationAdmin; - private final TbQueueAdmin edgeAdmin; - - public RabbitMqTbCoreQueueFactory(TbRabbitMqSettings rabbitMqSettings, - TbQueueCoreSettings coreSettings, - TbQueueTransportApiSettings transportApiSettings, - TbQueueRuleEngineSettings ruleEngineSettings, - TopicService topicService, - TbServiceInfoProvider serviceInfoProvider, - TbQueueRemoteJsInvokeSettings jsInvokeSettings, - TbQueueTransportNotificationSettings transportNotificationSettings, - TbQueueEdgeSettings edgeSettings, - TbRabbitMqQueueArguments queueArguments) { - this.rabbitMqSettings = rabbitMqSettings; - this.coreSettings = coreSettings; - this.transportApiSettings = transportApiSettings; - this.ruleEngineSettings = ruleEngineSettings; - this.topicService = topicService; - this.serviceInfoProvider = serviceInfoProvider; - this.jsInvokeSettings = jsInvokeSettings; - this.transportNotificationSettings = transportNotificationSettings; - this.edgeSettings = edgeSettings; - - this.coreAdmin = new TbRabbitMqAdmin(rabbitMqSettings, queueArguments.getCoreArgs()); - this.ruleEngineAdmin = new TbRabbitMqAdmin(rabbitMqSettings, queueArguments.getRuleEngineArgs()); - this.jsExecutorAdmin = new TbRabbitMqAdmin(rabbitMqSettings, queueArguments.getJsExecutorArgs()); - this.transportApiAdmin = new TbRabbitMqAdmin(rabbitMqSettings, queueArguments.getTransportApiArgs()); - this.notificationAdmin = new TbRabbitMqAdmin(rabbitMqSettings, queueArguments.getNotificationsArgs()); - this.edgeAdmin = new TbRabbitMqAdmin(rabbitMqSettings, queueArguments.getEdgeArgs()); - } - - @Override - public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); - } - - @Override - public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, - topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName()); - } - - @Override - public TbQueueConsumer> createToCoreMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToCoreNotificationsMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(notificationAdmin, rabbitMqSettings, - topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createEdgeMsgProducer() { - return new TbRabbitMqProducerTemplate<>(edgeAdmin, rabbitMqSettings, topicService.buildTopicName(edgeSettings.getTopic())); - } - - @Override - public TbQueueConsumer> createEdgeMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(edgeAdmin, rabbitMqSettings, topicService.buildTopicName(edgeSettings.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToEdgeMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToEdgeNotificationsMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(notificationAdmin, rabbitMqSettings, - topicService.getEdgeNotificationsTopic(serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToEdgeNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createEdgeNotificationsMsgProducer() { - return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, - topicService.getEdgeNotificationsTopic(serviceInfoProvider.getServiceId()).getFullTopicName()); - } - - @Override - public TbQueueConsumer> createTransportApiRequestConsumer() { - return new TbRabbitMqConsumerTemplate<>(transportApiAdmin, rabbitMqSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createTransportApiResponseProducer() { - return new TbRabbitMqProducerTemplate<>(transportApiAdmin, rabbitMqSettings, topicService.buildTopicName(transportApiSettings.getResponsesTopic())); - } - - @Override - @Bean - public TbQueueRequestTemplate, TbProtoQueueMsg> createRemoteJsRequestTemplate() { - TbQueueProducer> producer = new TbRabbitMqProducerTemplate<>(jsExecutorAdmin, rabbitMqSettings, jsInvokeSettings.getRequestTopic()); - TbQueueConsumer> consumer = new TbRabbitMqConsumerTemplate<>(jsExecutorAdmin, rabbitMqSettings, - jsInvokeSettings.getResponseTopic() + "." + serviceInfoProvider.getServiceId(), - msg -> { - JsInvokeProtos.RemoteJsResponse.Builder builder = JsInvokeProtos.RemoteJsResponse.newBuilder(); - JsonFormat.parser().ignoringUnknownFields().merge(new String(msg.getData(), StandardCharsets.UTF_8), builder); - return new TbProtoQueueMsg<>(msg.getKey(), builder.build(), msg.getHeaders()); - }); - - DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder - , TbProtoQueueMsg> builder = DefaultTbQueueRequestTemplate.builder(); - builder.queueAdmin(jsExecutorAdmin); - builder.requestTemplate(producer); - builder.responseTemplate(consumer); - builder.maxPendingRequests(jsInvokeSettings.getMaxPendingRequests()); - builder.maxRequestTimeout(jsInvokeSettings.getMaxRequestsTimeout()); - builder.pollInterval(jsInvokeSettings.getResponsePollInterval()); - return builder.build(); - } - - @Override - public TbQueueProducer> createVersionControlMsgProducer() { - //TODO: version-control - return null; - } - - @Override - public TbQueueConsumer> createToUsageStatsServiceMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); - } - - @Override - public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); - } - - @Override - public TbQueueProducer> createHousekeeperMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic())); - } - - @Override - public TbQueueConsumer> createHousekeeperMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToHousekeeperServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createHousekeeperReprocessingMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getHousekeeperReprocessingTopic())); - } - - @Override - public TbQueueConsumer> createHousekeeperReprocessingMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getHousekeeperReprocessingTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToHousekeeperServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @PreDestroy - private void destroy() { - if (coreAdmin != null) { - coreAdmin.destroy(); - } - if (ruleEngineAdmin != null) { - ruleEngineAdmin.destroy(); - } - if (jsExecutorAdmin != null) { - jsExecutorAdmin.destroy(); - } - if (transportApiAdmin != null) { - transportApiAdmin.destroy(); - } - if (notificationAdmin != null) { - notificationAdmin.destroy(); - } - if (edgeAdmin != null) { - edgeAdmin.destroy(); - } - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbRuleEngineQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbRuleEngineQueueFactory.java deleted file mode 100644 index 3dabaf100b..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbRuleEngineQueueFactory.java +++ /dev/null @@ -1,202 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.provider; - -import com.google.protobuf.util.JsonFormat; -import jakarta.annotation.PreDestroy; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.context.annotation.Bean; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.queue.Queue; -import org.thingsboard.server.common.msg.queue.ServiceType; -import org.thingsboard.server.gen.js.JsInvokeProtos; -import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToEdgeMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToHousekeeperServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueConsumer; -import org.thingsboard.server.queue.TbQueueProducer; -import org.thingsboard.server.queue.TbQueueRequestTemplate; -import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; -import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; -import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; -import org.thingsboard.server.queue.discovery.TopicService; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqAdmin; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqConsumerTemplate; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqProducerTemplate; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqQueueArguments; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqSettings; -import org.thingsboard.server.queue.settings.TbQueueCoreSettings; -import org.thingsboard.server.queue.settings.TbQueueEdgeSettings; -import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings; -import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; - -import java.nio.charset.StandardCharsets; - -@Component -@ConditionalOnExpression("'${queue.type:null}'=='rabbitmq' && '${service.type:null}'=='tb-rule-engine'") -public class RabbitMqTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { - - private final TopicService topicService; - private final TbQueueCoreSettings coreSettings; - private final TbServiceInfoProvider serviceInfoProvider; - private final TbQueueRuleEngineSettings ruleEngineSettings; - private final TbRabbitMqSettings rabbitMqSettings; - private final TbQueueRemoteJsInvokeSettings jsInvokeSettings; - private final TbQueueTransportNotificationSettings transportNotificationSettings; - private final TbQueueEdgeSettings edgeSettings; - - private final TbQueueAdmin coreAdmin; - private final TbQueueAdmin ruleEngineAdmin; - private final TbQueueAdmin jsExecutorAdmin; - private final TbQueueAdmin notificationAdmin; - private final TbQueueAdmin edgeAdmin; - - public RabbitMqTbRuleEngineQueueFactory(TopicService topicService, TbQueueCoreSettings coreSettings, - TbQueueRuleEngineSettings ruleEngineSettings, - TbServiceInfoProvider serviceInfoProvider, - TbRabbitMqSettings rabbitMqSettings, - TbQueueRemoteJsInvokeSettings jsInvokeSettings, - TbQueueTransportNotificationSettings transportNotificationSettings, - TbQueueEdgeSettings edgeSettings, - TbRabbitMqQueueArguments queueArguments) { - this.topicService = topicService; - this.coreSettings = coreSettings; - this.serviceInfoProvider = serviceInfoProvider; - this.ruleEngineSettings = ruleEngineSettings; - this.rabbitMqSettings = rabbitMqSettings; - this.jsInvokeSettings = jsInvokeSettings; - this.transportNotificationSettings = transportNotificationSettings; - this.edgeSettings = edgeSettings; - - this.coreAdmin = new TbRabbitMqAdmin(rabbitMqSettings, queueArguments.getCoreArgs()); - this.ruleEngineAdmin = new TbRabbitMqAdmin(rabbitMqSettings, queueArguments.getRuleEngineArgs()); - this.jsExecutorAdmin = new TbRabbitMqAdmin(rabbitMqSettings, queueArguments.getJsExecutorArgs()); - this.notificationAdmin = new TbRabbitMqAdmin(rabbitMqSettings, queueArguments.getNotificationsArgs()); - this.edgeAdmin = new TbRabbitMqAdmin(rabbitMqSettings, queueArguments.getEdgeArgs()); - } - - @Override - public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); - } - - @Override - public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbRabbitMqProducerTemplate<>(ruleEngineAdmin, rabbitMqSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); - } - - @Override - public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueProducer> createEdgeMsgProducer() { - return new TbRabbitMqProducerTemplate<>(edgeAdmin, rabbitMqSettings, topicService.buildTopicName(edgeSettings.getTopic())); - } - - @Override - public TbQueueProducer> createEdgeNotificationsMsgProducer() { - return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, topicService.getEdgeNotificationsTopic(serviceInfoProvider.getServiceId()).getFullTopicName()); - } - - @Override - public TbQueueConsumer> createToRuleEngineMsgConsumer(Queue configuration) { - return new TbRabbitMqConsumerTemplate<>(ruleEngineAdmin, rabbitMqSettings, topicService.buildTopicName(configuration.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToRuleEngineNotificationsMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(notificationAdmin, rabbitMqSettings, - topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - @Bean - public TbQueueRequestTemplate, TbProtoQueueMsg> createRemoteJsRequestTemplate() { - TbQueueProducer> producer = new TbRabbitMqProducerTemplate<>(jsExecutorAdmin, rabbitMqSettings, jsInvokeSettings.getRequestTopic()); - TbQueueConsumer> consumer = new TbRabbitMqConsumerTemplate<>(jsExecutorAdmin, rabbitMqSettings, - jsInvokeSettings.getResponseTopic() + "." + serviceInfoProvider.getServiceId(), - msg -> { - JsInvokeProtos.RemoteJsResponse.Builder builder = JsInvokeProtos.RemoteJsResponse.newBuilder(); - JsonFormat.parser().ignoringUnknownFields().merge(new String(msg.getData(), StandardCharsets.UTF_8), builder); - return new TbProtoQueueMsg<>(msg.getKey(), builder.build(), msg.getHeaders()); - }); - - DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder - , TbProtoQueueMsg> builder = DefaultTbQueueRequestTemplate.builder(); - builder.queueAdmin(jsExecutorAdmin); - builder.requestTemplate(producer); - builder.responseTemplate(consumer); - builder.maxPendingRequests(jsInvokeSettings.getMaxPendingRequests()); - builder.maxRequestTimeout(jsInvokeSettings.getMaxRequestsTimeout()); - builder.pollInterval(jsInvokeSettings.getResponsePollInterval()); - return builder.build(); - } - - @Override - public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); - } - - @Override - public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); - } - - @Override - public TbQueueProducer> createHousekeeperMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic())); - } - - @PreDestroy - private void destroy() { - if (coreAdmin != null) { - coreAdmin.destroy(); - } - if (ruleEngineAdmin != null) { - ruleEngineAdmin.destroy(); - } - if (jsExecutorAdmin != null) { - jsExecutorAdmin.destroy(); - } - if (notificationAdmin != null) { - notificationAdmin.destroy(); - } - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbVersionControlQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbVersionControlQueueFactory.java deleted file mode 100644 index 604955be2c..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbVersionControlQueueFactory.java +++ /dev/null @@ -1,98 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.provider; - -import jakarta.annotation.PreDestroy; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.stereotype.Component; -import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueConsumer; -import org.thingsboard.server.queue.TbQueueProducer; -import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.TopicService; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqAdmin; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqConsumerTemplate; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqProducerTemplate; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqQueueArguments; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqSettings; -import org.thingsboard.server.queue.settings.TbQueueCoreSettings; -import org.thingsboard.server.queue.settings.TbQueueVersionControlSettings; - -@Component -@ConditionalOnExpression("'${queue.type:null}'=='rabbitmq' && '${service.type:null}'=='tb-vc-executor'") -public class RabbitMqTbVersionControlQueueFactory implements TbVersionControlQueueFactory { - - private final TbRabbitMqSettings rabbitMqSettings; - private final TbQueueCoreSettings coreSettings; - private final TbQueueVersionControlSettings vcSettings; - private final TopicService topicService; - - private final TbQueueAdmin coreAdmin; - private final TbQueueAdmin notificationAdmin; - private final TbQueueAdmin vcAdmin; - - public RabbitMqTbVersionControlQueueFactory(TbRabbitMqSettings rabbitMqSettings, - TbQueueCoreSettings coreSettings, - TbQueueVersionControlSettings vcSettings, - TbRabbitMqQueueArguments queueArguments, - TopicService topicService - ) { - this.rabbitMqSettings = rabbitMqSettings; - this.coreSettings = coreSettings; - this.vcSettings = vcSettings; - this.topicService = topicService; - - this.coreAdmin = new TbRabbitMqAdmin(this.rabbitMqSettings, queueArguments.getCoreArgs()); - this.notificationAdmin = new TbRabbitMqAdmin(this.rabbitMqSettings, queueArguments.getNotificationsArgs()); - this.vcAdmin = new TbRabbitMqAdmin(this.rabbitMqSettings, queueArguments.getVcArgs()); - } - - @Override - public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); - } - - @Override - public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueConsumer> createToVersionControlMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(vcAdmin, rabbitMqSettings, topicService.buildTopicName(vcSettings.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders()) - ); - } - - @Override - public TbQueueProducer> createHousekeeperMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic())); - } - - @PreDestroy - private void destroy() { - if (coreAdmin != null) { - coreAdmin.destroy(); - } - if (notificationAdmin != null) { - notificationAdmin.destroy(); - } - if (vcAdmin != null) { - vcAdmin.destroy(); - } - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTransportQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTransportQueueFactory.java deleted file mode 100644 index ea922557bd..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTransportQueueFactory.java +++ /dev/null @@ -1,154 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.provider; - -import jakarta.annotation.PreDestroy; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.stereotype.Component; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToHousekeeperServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueConsumer; -import org.thingsboard.server.queue.TbQueueProducer; -import org.thingsboard.server.queue.TbQueueRequestTemplate; -import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; -import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; -import org.thingsboard.server.queue.discovery.TopicService; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqAdmin; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqConsumerTemplate; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqProducerTemplate; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqQueueArguments; -import org.thingsboard.server.queue.rabbitmq.TbRabbitMqSettings; -import org.thingsboard.server.queue.settings.TbQueueCoreSettings; -import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; - -@Component -@ConditionalOnExpression("'${queue.type:null}'=='rabbitmq' && (('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true') || '${service.type:null}'=='tb-transport')") -@Slf4j -public class RabbitMqTransportQueueFactory implements TbTransportQueueFactory { - private final TbQueueTransportApiSettings transportApiSettings; - private final TbQueueTransportNotificationSettings transportNotificationSettings; - private final TbRabbitMqSettings rabbitMqSettings; - private final TbServiceInfoProvider serviceInfoProvider; - private final TbQueueCoreSettings coreSettings; - private final TbQueueRuleEngineSettings ruleEngineSettings; - private final TopicService topicService; - - private final TbQueueAdmin coreAdmin; - private final TbQueueAdmin ruleEngineAdmin; - private final TbQueueAdmin transportApiAdmin; - private final TbQueueAdmin notificationAdmin; - - public RabbitMqTransportQueueFactory(TbQueueTransportApiSettings transportApiSettings, - TbQueueTransportNotificationSettings transportNotificationSettings, - TbRabbitMqSettings rabbitMqSettings, - TbServiceInfoProvider serviceInfoProvider, - TbQueueCoreSettings coreSettings, - TbQueueRuleEngineSettings ruleEngineSettings, - TbRabbitMqQueueArguments queueArguments, - TopicService topicService) { - this.transportApiSettings = transportApiSettings; - this.transportNotificationSettings = transportNotificationSettings; - this.rabbitMqSettings = rabbitMqSettings; - this.serviceInfoProvider = serviceInfoProvider; - this.coreSettings = coreSettings; - this.ruleEngineSettings = ruleEngineSettings; - this.topicService = topicService; - - this.coreAdmin = new TbRabbitMqAdmin(rabbitMqSettings, queueArguments.getCoreArgs()); - this.ruleEngineAdmin = new TbRabbitMqAdmin(rabbitMqSettings, queueArguments.getRuleEngineArgs()); - this.transportApiAdmin = new TbRabbitMqAdmin(rabbitMqSettings, queueArguments.getTransportApiArgs()); - this.notificationAdmin = new TbRabbitMqAdmin(rabbitMqSettings, queueArguments.getNotificationsArgs()); - } - - @Override - public TbQueueRequestTemplate, TbProtoQueueMsg> createTransportApiRequestTemplate() { - TbQueueProducer> producerTemplate = - new TbRabbitMqProducerTemplate<>(transportApiAdmin, rabbitMqSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic())); - - TbQueueConsumer> consumerTemplate = - new TbRabbitMqConsumerTemplate<>(transportApiAdmin, rabbitMqSettings, - topicService.buildTopicName(transportApiSettings.getResponsesTopic() + "." + serviceInfoProvider.getServiceId()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiResponseMsg.parseFrom(msg.getData()), msg.getHeaders())); - - DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder - , TbProtoQueueMsg> templateBuilder = DefaultTbQueueRequestTemplate.builder(); - templateBuilder.queueAdmin(transportApiAdmin); - templateBuilder.requestTemplate(producerTemplate); - templateBuilder.responseTemplate(consumerTemplate); - templateBuilder.maxPendingRequests(transportApiSettings.getMaxPendingRequests()); - templateBuilder.maxRequestTimeout(transportApiSettings.getMaxRequestsTimeout()); - templateBuilder.pollInterval(transportApiSettings.getResponsePollInterval()); - return templateBuilder.build(); - } - - @Override - public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbRabbitMqProducerTemplate<>(ruleEngineAdmin, rabbitMqSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueConsumer> createTransportNotificationsConsumer() { - return new TbRabbitMqConsumerTemplate<>(notificationAdmin, rabbitMqSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic() + "." + serviceInfoProvider.getServiceId()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToTransportMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); - } - - @Override - public TbQueueProducer> createHousekeeperMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic())); - } - - @PreDestroy - private void destroy() { - if (coreAdmin != null) { - coreAdmin.destroy(); - } - if (ruleEngineAdmin != null) { - ruleEngineAdmin.destroy(); - } - if (transportApiAdmin != null) { - transportApiAdmin.destroy(); - } - if (notificationAdmin != null) { - notificationAdmin.destroy(); - } - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusMonolithQueueFactory.java deleted file mode 100644 index fa7e73b879..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusMonolithQueueFactory.java +++ /dev/null @@ -1,306 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.provider; - -import com.google.protobuf.util.JsonFormat; -import jakarta.annotation.PreDestroy; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.context.annotation.Bean; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.queue.Queue; -import org.thingsboard.server.common.msg.queue.ServiceType; -import org.thingsboard.server.gen.js.JsInvokeProtos; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToEdgeMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToEdgeNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToHousekeeperServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToVersionControlServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueConsumer; -import org.thingsboard.server.queue.TbQueueProducer; -import org.thingsboard.server.queue.TbQueueRequestTemplate; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusAdmin; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusConsumerTemplate; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusProducerTemplate; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusQueueConfigs; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusSettings; -import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; -import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; -import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; -import org.thingsboard.server.queue.discovery.TopicService; -import org.thingsboard.server.queue.settings.TbQueueCoreSettings; -import org.thingsboard.server.queue.settings.TbQueueEdgeSettings; -import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings; -import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; -import org.thingsboard.server.queue.settings.TbQueueVersionControlSettings; - -import java.nio.charset.StandardCharsets; - -@Component -@ConditionalOnExpression("'${queue.type:null}'=='service-bus' && '${service.type:null}'=='monolith'") -public class ServiceBusMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngineQueueFactory, TbVersionControlQueueFactory { - - private final TopicService topicService; - private final TbQueueCoreSettings coreSettings; - private final TbServiceInfoProvider serviceInfoProvider; - private final TbQueueRuleEngineSettings ruleEngineSettings; - private final TbQueueTransportApiSettings transportApiSettings; - private final TbQueueTransportNotificationSettings transportNotificationSettings; - private final TbServiceBusSettings serviceBusSettings; - private final TbQueueRemoteJsInvokeSettings jsInvokeSettings; - private final TbQueueVersionControlSettings vcSettings; - private final TbQueueEdgeSettings edgeSettings; - - private final TbQueueAdmin coreAdmin; - private final TbQueueAdmin ruleEngineAdmin; - private final TbQueueAdmin jsExecutorAdmin; - private final TbQueueAdmin transportApiAdmin; - private final TbQueueAdmin notificationAdmin; - private final TbQueueAdmin vcAdmin; - private final TbQueueAdmin edgeAdmin; - - public ServiceBusMonolithQueueFactory(TopicService topicService, TbQueueCoreSettings coreSettings, - TbQueueRuleEngineSettings ruleEngineSettings, - TbServiceInfoProvider serviceInfoProvider, - TbQueueTransportApiSettings transportApiSettings, - TbQueueTransportNotificationSettings transportNotificationSettings, - TbServiceBusSettings serviceBusSettings, - TbQueueRemoteJsInvokeSettings jsInvokeSettings, - TbQueueVersionControlSettings vcSettings, - TbQueueEdgeSettings edgeSettings, - TbServiceBusQueueConfigs serviceBusQueueConfigs) { - this.topicService = topicService; - this.coreSettings = coreSettings; - this.serviceInfoProvider = serviceInfoProvider; - this.ruleEngineSettings = ruleEngineSettings; - this.transportApiSettings = transportApiSettings; - this.transportNotificationSettings = transportNotificationSettings; - this.serviceBusSettings = serviceBusSettings; - this.jsInvokeSettings = jsInvokeSettings; - this.vcSettings = vcSettings; - this.edgeSettings = edgeSettings; - - this.coreAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getCoreConfigs()); - this.ruleEngineAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getRuleEngineConfigs()); - this.jsExecutorAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getJsExecutorConfigs()); - this.transportApiAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getTransportApiConfigs()); - this.notificationAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getNotificationsConfigs()); - this.vcAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getVcConfigs()); - this.edgeAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getEdgeConfigs()); - } - - @Override - public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); - } - - @Override - public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbServiceBusProducerTemplate<>(ruleEngineAdmin, serviceBusSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); - } - - @Override - public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, - topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName()); - } - - @Override - public TbQueueConsumer> createToVersionControlMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(vcAdmin, serviceBusSettings, topicService.buildTopicName(vcSettings.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders()) - ); - } - - @Override - public TbQueueConsumer> createToRuleEngineMsgConsumer(Queue configuration) { - return new TbServiceBusConsumerTemplate<>(ruleEngineAdmin, serviceBusSettings, configuration.getTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToRuleEngineNotificationsMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(notificationAdmin, serviceBusSettings, - topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToCoreMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToCoreNotificationsMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(notificationAdmin, serviceBusSettings, - topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createTransportApiRequestConsumer() { - return new TbServiceBusConsumerTemplate<>(transportApiAdmin, serviceBusSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createTransportApiResponseProducer() { - return new TbServiceBusProducerTemplate<>(transportApiAdmin, serviceBusSettings, topicService.buildTopicName(transportApiSettings.getResponsesTopic())); - } - - @Override - @Bean - public TbQueueRequestTemplate, TbProtoQueueMsg> createRemoteJsRequestTemplate() { - TbQueueProducer> producer = new TbServiceBusProducerTemplate<>(jsExecutorAdmin, serviceBusSettings, jsInvokeSettings.getRequestTopic()); - TbQueueConsumer> consumer = new TbServiceBusConsumerTemplate<>(jsExecutorAdmin, serviceBusSettings, - jsInvokeSettings.getResponseTopic() + "." + serviceInfoProvider.getServiceId(), - msg -> { - JsInvokeProtos.RemoteJsResponse.Builder builder = JsInvokeProtos.RemoteJsResponse.newBuilder(); - JsonFormat.parser().ignoringUnknownFields().merge(new String(msg.getData(), StandardCharsets.UTF_8), builder); - return new TbProtoQueueMsg<>(msg.getKey(), builder.build(), msg.getHeaders()); - }); - - DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder - , TbProtoQueueMsg> builder = DefaultTbQueueRequestTemplate.builder(); - builder.queueAdmin(jsExecutorAdmin); - builder.requestTemplate(producer); - builder.responseTemplate(consumer); - builder.maxPendingRequests(jsInvokeSettings.getMaxPendingRequests()); - builder.maxRequestTimeout(jsInvokeSettings.getMaxRequestsTimeout()); - builder.pollInterval(jsInvokeSettings.getResponsePollInterval()); - return builder.build(); - } - - @Override - public TbQueueConsumer> createToUsageStatsServiceMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); - } - - @Override - public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); - } - - @Override - public TbQueueProducer> createVersionControlMsgProducer() { - return new TbServiceBusProducerTemplate<>(vcAdmin, serviceBusSettings, topicService.buildTopicName(vcSettings.getTopic())); - } - - @Override - public TbQueueProducer> createHousekeeperMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic())); - } - - @Override - public TbQueueConsumer> createHousekeeperMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToHousekeeperServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createHousekeeperReprocessingMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getHousekeeperReprocessingTopic())); - } - - @Override - public TbQueueConsumer> createHousekeeperReprocessingMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getHousekeeperReprocessingTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToHousekeeperServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createEdgeMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(edgeAdmin, serviceBusSettings, topicService.buildTopicName(edgeSettings.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToEdgeMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createEdgeMsgProducer() { - return new TbServiceBusProducerTemplate<>(edgeAdmin, serviceBusSettings, topicService.buildTopicName(edgeSettings.getTopic())); - } - - @Override - public TbQueueConsumer> createToEdgeNotificationsMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(notificationAdmin, serviceBusSettings, - topicService.getEdgeNotificationsTopic(serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToEdgeNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createEdgeNotificationsMsgProducer() { - return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, - topicService.getEdgeNotificationsTopic(serviceInfoProvider.getServiceId()).getFullTopicName()); - } - - @PreDestroy - private void destroy() { - if (coreAdmin != null) { - coreAdmin.destroy(); - } - if (ruleEngineAdmin != null) { - ruleEngineAdmin.destroy(); - } - if (jsExecutorAdmin != null) { - jsExecutorAdmin.destroy(); - } - if (transportApiAdmin != null) { - transportApiAdmin.destroy(); - } - if (notificationAdmin != null) { - notificationAdmin.destroy(); - } - if (vcAdmin != null) { - vcAdmin.destroy(); - } - if (edgeAdmin != null) { - edgeAdmin.destroy(); - } - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbCoreQueueFactory.java deleted file mode 100644 index 71a7efe50b..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbCoreQueueFactory.java +++ /dev/null @@ -1,278 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.provider; - -import com.google.protobuf.util.JsonFormat; -import jakarta.annotation.PreDestroy; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.context.annotation.Bean; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.msg.queue.ServiceType; -import org.thingsboard.server.gen.js.JsInvokeProtos; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToEdgeMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToEdgeNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToHousekeeperServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToVersionControlServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueConsumer; -import org.thingsboard.server.queue.TbQueueProducer; -import org.thingsboard.server.queue.TbQueueRequestTemplate; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusAdmin; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusConsumerTemplate; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusProducerTemplate; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusQueueConfigs; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusSettings; -import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; -import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; -import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; -import org.thingsboard.server.queue.discovery.TopicService; -import org.thingsboard.server.queue.settings.TbQueueCoreSettings; -import org.thingsboard.server.queue.settings.TbQueueEdgeSettings; -import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings; -import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; - -import java.nio.charset.StandardCharsets; - -@Component -@ConditionalOnExpression("'${queue.type:null}'=='service-bus' && '${service.type:null}'=='tb-core'") -public class ServiceBusTbCoreQueueFactory implements TbCoreQueueFactory { - - private final TbServiceBusSettings serviceBusSettings; - private final TbQueueRuleEngineSettings ruleEngineSettings; - private final TbQueueCoreSettings coreSettings; - private final TbQueueTransportApiSettings transportApiSettings; - private final TopicService topicService; - private final TbServiceInfoProvider serviceInfoProvider; - private final TbQueueRemoteJsInvokeSettings jsInvokeSettings; - private final TbQueueTransportNotificationSettings transportNotificationSettings; - private final TbQueueEdgeSettings edgeSettings; - - private final TbQueueAdmin coreAdmin; - private final TbQueueAdmin ruleEngineAdmin; - private final TbQueueAdmin jsExecutorAdmin; - private final TbQueueAdmin transportApiAdmin; - private final TbQueueAdmin notificationAdmin; - private final TbQueueAdmin edgeAdmin; - - public ServiceBusTbCoreQueueFactory(TbServiceBusSettings serviceBusSettings, - TbQueueCoreSettings coreSettings, - TbQueueTransportApiSettings transportApiSettings, - TbQueueRuleEngineSettings ruleEngineSettings, - TopicService topicService, - TbServiceInfoProvider serviceInfoProvider, - TbQueueRemoteJsInvokeSettings jsInvokeSettings, - TbQueueTransportNotificationSettings transportNotificationSettings, - TbQueueEdgeSettings edgeSettings, - TbServiceBusQueueConfigs serviceBusQueueConfigs) { - this.serviceBusSettings = serviceBusSettings; - this.coreSettings = coreSettings; - this.transportApiSettings = transportApiSettings; - this.ruleEngineSettings = ruleEngineSettings; - this.topicService = topicService; - this.serviceInfoProvider = serviceInfoProvider; - this.jsInvokeSettings = jsInvokeSettings; - this.transportNotificationSettings = transportNotificationSettings; - this.edgeSettings = edgeSettings; - - this.coreAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getCoreConfigs()); - this.ruleEngineAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getRuleEngineConfigs()); - this.jsExecutorAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getJsExecutorConfigs()); - this.transportApiAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getTransportApiConfigs()); - this.notificationAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getNotificationsConfigs()); - this.edgeAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getEdgeConfigs()); - } - - @Override - public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); - } - - @Override - public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, - topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName()); - } - - @Override - public TbQueueConsumer> createToCoreMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToCoreNotificationsMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(notificationAdmin, serviceBusSettings, - topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createTransportApiRequestConsumer() { - return new TbServiceBusConsumerTemplate<>(transportApiAdmin, serviceBusSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createTransportApiResponseProducer() { - return new TbServiceBusProducerTemplate<>(transportApiAdmin, serviceBusSettings, topicService.buildTopicName(transportApiSettings.getResponsesTopic())); - } - - @Override - @Bean - public TbQueueRequestTemplate, TbProtoQueueMsg> createRemoteJsRequestTemplate() { - TbQueueProducer> producer = new TbServiceBusProducerTemplate<>(jsExecutorAdmin, serviceBusSettings, jsInvokeSettings.getRequestTopic()); - TbQueueConsumer> consumer = new TbServiceBusConsumerTemplate<>(jsExecutorAdmin, serviceBusSettings, - jsInvokeSettings.getResponseTopic() + "." + serviceInfoProvider.getServiceId(), - msg -> { - JsInvokeProtos.RemoteJsResponse.Builder builder = JsInvokeProtos.RemoteJsResponse.newBuilder(); - JsonFormat.parser().ignoringUnknownFields().merge(new String(msg.getData(), StandardCharsets.UTF_8), builder); - return new TbProtoQueueMsg<>(msg.getKey(), builder.build(), msg.getHeaders()); - }); - - DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder - , TbProtoQueueMsg> builder = DefaultTbQueueRequestTemplate.builder(); - builder.queueAdmin(jsExecutorAdmin); - builder.requestTemplate(producer); - builder.responseTemplate(consumer); - builder.maxPendingRequests(jsInvokeSettings.getMaxPendingRequests()); - builder.maxRequestTimeout(jsInvokeSettings.getMaxRequestsTimeout()); - builder.pollInterval(jsInvokeSettings.getResponsePollInterval()); - return builder.build(); - } - - @Override - public TbQueueConsumer> createToUsageStatsServiceMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); - } - - @Override - public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); - } - - @Override - public TbQueueProducer> createVersionControlMsgProducer() { - //TODO: version-control - return null; - } - - @Override - public TbQueueProducer> createHousekeeperMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic())); - } - - @Override - public TbQueueConsumer> createHousekeeperMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToHousekeeperServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createHousekeeperReprocessingMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getHousekeeperReprocessingTopic())); - } - - @Override - public TbQueueConsumer> createHousekeeperReprocessingMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getHousekeeperReprocessingTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToHousekeeperServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createEdgeMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(edgeAdmin, serviceBusSettings, topicService.buildTopicName(edgeSettings.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToEdgeMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createEdgeMsgProducer() { - return new TbServiceBusProducerTemplate<>(edgeAdmin, serviceBusSettings, topicService.buildTopicName(edgeSettings.getTopic())); - } - - @Override - public TbQueueConsumer> createToEdgeNotificationsMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(notificationAdmin, serviceBusSettings, - topicService.getEdgeNotificationsTopic(serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToEdgeNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createEdgeNotificationsMsgProducer() { - return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, - topicService.getEdgeNotificationsTopic(serviceInfoProvider.getServiceId()).getFullTopicName()); - } - - @PreDestroy - private void destroy() { - if (coreAdmin != null) { - coreAdmin.destroy(); - } - if (ruleEngineAdmin != null) { - ruleEngineAdmin.destroy(); - } - if (jsExecutorAdmin != null) { - jsExecutorAdmin.destroy(); - } - if (transportApiAdmin != null) { - transportApiAdmin.destroy(); - } - if (notificationAdmin != null) { - notificationAdmin.destroy(); - } - if (edgeAdmin != null) { - edgeAdmin.destroy(); - } - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbRuleEngineQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbRuleEngineQueueFactory.java deleted file mode 100644 index 643a9dee3b..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbRuleEngineQueueFactory.java +++ /dev/null @@ -1,202 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.provider; - -import com.google.protobuf.util.JsonFormat; -import jakarta.annotation.PreDestroy; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.context.annotation.Bean; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.queue.Queue; -import org.thingsboard.server.common.msg.queue.ServiceType; -import org.thingsboard.server.gen.js.JsInvokeProtos; -import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToEdgeMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToHousekeeperServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueConsumer; -import org.thingsboard.server.queue.TbQueueProducer; -import org.thingsboard.server.queue.TbQueueRequestTemplate; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusAdmin; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusConsumerTemplate; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusProducerTemplate; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusQueueConfigs; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusSettings; -import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; -import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; -import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; -import org.thingsboard.server.queue.discovery.TopicService; -import org.thingsboard.server.queue.settings.TbQueueCoreSettings; -import org.thingsboard.server.queue.settings.TbQueueEdgeSettings; -import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings; -import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; - -import java.nio.charset.StandardCharsets; - -@Component -@ConditionalOnExpression("'${queue.type:null}'=='service-bus' && '${service.type:null}'=='tb-rule-engine'") -public class ServiceBusTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { - - private final TopicService topicService; - private final TbQueueCoreSettings coreSettings; - private final TbServiceInfoProvider serviceInfoProvider; - private final TbQueueRuleEngineSettings ruleEngineSettings; - private final TbServiceBusSettings serviceBusSettings; - private final TbQueueRemoteJsInvokeSettings jsInvokeSettings; - private final TbQueueTransportNotificationSettings transportNotificationSettings; - private final TbQueueEdgeSettings edgeSettings; - - private final TbQueueAdmin coreAdmin; - private final TbQueueAdmin ruleEngineAdmin; - private final TbQueueAdmin jsExecutorAdmin; - private final TbQueueAdmin notificationAdmin; - private final TbQueueAdmin edgeAdmin; - - public ServiceBusTbRuleEngineQueueFactory(TopicService topicService, TbQueueCoreSettings coreSettings, - TbQueueRuleEngineSettings ruleEngineSettings, - TbServiceInfoProvider serviceInfoProvider, - TbServiceBusSettings serviceBusSettings, - TbQueueRemoteJsInvokeSettings jsInvokeSettings, - TbQueueTransportNotificationSettings transportNotificationSettings, - TbQueueEdgeSettings edgeSettings, - TbServiceBusQueueConfigs serviceBusQueueConfigs) { - this.topicService = topicService; - this.coreSettings = coreSettings; - this.serviceInfoProvider = serviceInfoProvider; - this.ruleEngineSettings = ruleEngineSettings; - this.serviceBusSettings = serviceBusSettings; - this.jsInvokeSettings = jsInvokeSettings; - this.transportNotificationSettings = transportNotificationSettings; - this.edgeSettings = edgeSettings; - - this.coreAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getCoreConfigs()); - this.ruleEngineAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getRuleEngineConfigs()); - this.jsExecutorAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getJsExecutorConfigs()); - this.notificationAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getNotificationsConfigs()); - this.edgeAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getEdgeConfigs()); - } - - @Override - public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); - } - - @Override - public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbServiceBusProducerTemplate<>(ruleEngineAdmin, serviceBusSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); - } - - @Override - public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueProducer> createEdgeMsgProducer() { - return new TbServiceBusProducerTemplate<>(edgeAdmin, serviceBusSettings, topicService.buildTopicName(edgeSettings.getTopic())); - } - - @Override - public TbQueueProducer> createEdgeNotificationsMsgProducer() { - return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, topicService.getEdgeNotificationsTopic(serviceInfoProvider.getServiceId()).getFullTopicName()); - } - - @Override - public TbQueueConsumer> createToRuleEngineMsgConsumer(Queue configuration) { - return new TbServiceBusConsumerTemplate<>(ruleEngineAdmin, serviceBusSettings, topicService.buildTopicName(configuration.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueConsumer> createToRuleEngineNotificationsMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(notificationAdmin, serviceBusSettings, - topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - @Bean - public TbQueueRequestTemplate, TbProtoQueueMsg> createRemoteJsRequestTemplate() { - TbQueueProducer> producer = new TbServiceBusProducerTemplate<>(jsExecutorAdmin, serviceBusSettings, jsInvokeSettings.getRequestTopic()); - TbQueueConsumer> consumer = new TbServiceBusConsumerTemplate<>(jsExecutorAdmin, serviceBusSettings, - jsInvokeSettings.getResponseTopic() + "." + serviceInfoProvider.getServiceId(), - msg -> { - JsInvokeProtos.RemoteJsResponse.Builder builder = JsInvokeProtos.RemoteJsResponse.newBuilder(); - JsonFormat.parser().ignoringUnknownFields().merge(new String(msg.getData(), StandardCharsets.UTF_8), builder); - return new TbProtoQueueMsg<>(msg.getKey(), builder.build(), msg.getHeaders()); - }); - - DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder - , TbProtoQueueMsg> builder = DefaultTbQueueRequestTemplate.builder(); - builder.queueAdmin(jsExecutorAdmin); - builder.requestTemplate(producer); - builder.responseTemplate(consumer); - builder.maxPendingRequests(jsInvokeSettings.getMaxPendingRequests()); - builder.maxRequestTimeout(jsInvokeSettings.getMaxRequestsTimeout()); - builder.pollInterval(jsInvokeSettings.getResponsePollInterval()); - return builder.build(); - } - - @Override - public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); - } - - @Override - public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); - } - - @Override - public TbQueueProducer> createHousekeeperMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic())); - } - - @PreDestroy - private void destroy() { - if (coreAdmin != null) { - coreAdmin.destroy(); - } - if (ruleEngineAdmin != null) { - ruleEngineAdmin.destroy(); - } - if (jsExecutorAdmin != null) { - jsExecutorAdmin.destroy(); - } - if (notificationAdmin != null) { - notificationAdmin.destroy(); - } - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbVersionControlQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbVersionControlQueueFactory.java deleted file mode 100644 index 0c363488ce..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbVersionControlQueueFactory.java +++ /dev/null @@ -1,98 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.provider; - -import jakarta.annotation.PreDestroy; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.stereotype.Component; -import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueConsumer; -import org.thingsboard.server.queue.TbQueueProducer; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusAdmin; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusConsumerTemplate; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusProducerTemplate; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusQueueConfigs; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusSettings; -import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.TopicService; -import org.thingsboard.server.queue.settings.TbQueueCoreSettings; -import org.thingsboard.server.queue.settings.TbQueueVersionControlSettings; - -@Component -@ConditionalOnExpression("'${queue.type:null}'=='service-bus' && '${service.type:null}'=='tb-vc-executor'") -public class ServiceBusTbVersionControlQueueFactory implements TbVersionControlQueueFactory { - - private final TbServiceBusSettings serviceBusSettings; - private final TbQueueCoreSettings coreSettings; - private final TbQueueVersionControlSettings vcSettings; - private final TopicService topicService; - - private final TbQueueAdmin coreAdmin; - private final TbQueueAdmin notificationAdmin; - private final TbQueueAdmin vcAdmin; - - public ServiceBusTbVersionControlQueueFactory(TbServiceBusSettings serviceBusSettings, - TbQueueCoreSettings coreSettings, - TbQueueVersionControlSettings vcSettings, - TbServiceBusQueueConfigs serviceBusQueueConfigs, - TopicService topicService - ) { - this.serviceBusSettings = serviceBusSettings; - this.coreSettings = coreSettings; - this.vcSettings = vcSettings; - this.topicService = topicService; - - this.coreAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getCoreConfigs()); - this.notificationAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getNotificationsConfigs()); - this.vcAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getVcConfigs()); - } - - @Override - public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); - } - - @Override - public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueConsumer> createToVersionControlMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(vcAdmin, serviceBusSettings, topicService.buildTopicName(vcSettings.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders()) - ); - } - - @Override - public TbQueueProducer> createHousekeeperMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic())); - } - - @PreDestroy - private void destroy() { - if (coreAdmin != null) { - coreAdmin.destroy(); - } - if (notificationAdmin != null) { - notificationAdmin.destroy(); - } - if (vcAdmin != null) { - vcAdmin.destroy(); - } - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTransportQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTransportQueueFactory.java deleted file mode 100644 index 7f3e246eec..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTransportQueueFactory.java +++ /dev/null @@ -1,155 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.provider; - -import jakarta.annotation.PreDestroy; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.stereotype.Component; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToHousekeeperServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueConsumer; -import org.thingsboard.server.queue.TbQueueProducer; -import org.thingsboard.server.queue.TbQueueRequestTemplate; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusAdmin; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusConsumerTemplate; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusProducerTemplate; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusQueueConfigs; -import org.thingsboard.server.queue.azure.servicebus.TbServiceBusSettings; -import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; -import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; -import org.thingsboard.server.queue.discovery.TopicService; -import org.thingsboard.server.queue.settings.TbQueueCoreSettings; -import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; -import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; - -@Component -@ConditionalOnExpression("'${queue.type:null}'=='service-bus' && (('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true') || '${service.type:null}'=='tb-transport')") -@Slf4j -public class ServiceBusTransportQueueFactory implements TbTransportQueueFactory { - private final TbQueueTransportApiSettings transportApiSettings; - private final TbQueueTransportNotificationSettings transportNotificationSettings; - private final TbServiceBusSettings serviceBusSettings; - private final TbServiceInfoProvider serviceInfoProvider; - private final TbQueueCoreSettings coreSettings; - private final TbQueueRuleEngineSettings ruleEngineSettings; - private final TopicService topicService; - - private final TbQueueAdmin coreAdmin; - private final TbQueueAdmin transportApiAdmin; - private final TbQueueAdmin notificationAdmin; - private final TbQueueAdmin ruleEngineAdmin; - - public ServiceBusTransportQueueFactory(TbQueueTransportApiSettings transportApiSettings, - TbQueueTransportNotificationSettings transportNotificationSettings, - TbServiceBusSettings serviceBusSettings, - TbQueueRuleEngineSettings ruleEngineSettings, - TbServiceInfoProvider serviceInfoProvider, - TbQueueCoreSettings coreSettings, - TbServiceBusQueueConfigs serviceBusQueueConfigs, - TopicService topicService) { - this.transportApiSettings = transportApiSettings; - this.transportNotificationSettings = transportNotificationSettings; - this.serviceBusSettings = serviceBusSettings; - this.serviceInfoProvider = serviceInfoProvider; - this.coreSettings = coreSettings; - this.ruleEngineSettings = ruleEngineSettings; - this.topicService = topicService; - - this.coreAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getCoreConfigs()); - this.transportApiAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getTransportApiConfigs()); - this.notificationAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getNotificationsConfigs()); - this.ruleEngineAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getRuleEngineConfigs()); - } - - @Override - public TbQueueRequestTemplate, TbProtoQueueMsg> createTransportApiRequestTemplate() { - TbQueueProducer> producerTemplate = - new TbServiceBusProducerTemplate<>(transportApiAdmin, serviceBusSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic())); - - TbQueueConsumer> consumerTemplate = - new TbServiceBusConsumerTemplate<>(transportApiAdmin, serviceBusSettings, - topicService.buildTopicName(transportApiSettings.getResponsesTopic() + "." + serviceInfoProvider.getServiceId()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiResponseMsg.parseFrom(msg.getData()), msg.getHeaders())); - - DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder - , TbProtoQueueMsg> templateBuilder = DefaultTbQueueRequestTemplate.builder(); - templateBuilder.queueAdmin(transportApiAdmin); - templateBuilder.requestTemplate(producerTemplate); - templateBuilder.responseTemplate(consumerTemplate); - templateBuilder.maxPendingRequests(transportApiSettings.getMaxPendingRequests()); - templateBuilder.maxRequestTimeout(transportApiSettings.getMaxRequestsTimeout()); - templateBuilder.pollInterval(transportApiSettings.getResponsePollInterval()); - return templateBuilder.build(); - } - - @Override - public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbServiceBusProducerTemplate<>(ruleEngineAdmin, serviceBusSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getTopic())); - } - - @Override - public TbQueueConsumer> createTransportNotificationsConsumer() { - return new TbServiceBusConsumerTemplate<>(notificationAdmin, serviceBusSettings, - topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic() + "." + serviceInfoProvider.getServiceId()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToTransportMsg.parseFrom(msg.getData()), msg.getHeaders())); - } - - @Override - public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); - } - - @Override - public TbQueueProducer> createHousekeeperMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getHousekeeperTopic())); - } - - @PreDestroy - private void destroy() { - if (coreAdmin != null) { - coreAdmin.destroy(); - } - if (transportApiAdmin != null) { - transportApiAdmin.destroy(); - } - if (notificationAdmin != null) { - notificationAdmin.destroy(); - } - if (ruleEngineAdmin != null) { - ruleEngineAdmin.destroy(); - } - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubAdmin.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubAdmin.java deleted file mode 100644 index 8d4b5abde0..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubAdmin.java +++ /dev/null @@ -1,231 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.pubsub; - -import com.google.api.gax.rpc.AlreadyExistsException; -import com.google.cloud.pubsub.v1.SubscriptionAdminClient; -import com.google.cloud.pubsub.v1.SubscriptionAdminSettings; -import com.google.cloud.pubsub.v1.TopicAdminClient; -import com.google.cloud.pubsub.v1.TopicAdminSettings; -import com.google.protobuf.Duration; -import com.google.pubsub.v1.ListSubscriptionsRequest; -import com.google.pubsub.v1.ListTopicsRequest; -import com.google.pubsub.v1.ProjectName; -import com.google.pubsub.v1.ProjectSubscriptionName; -import com.google.pubsub.v1.Subscription; -import com.google.pubsub.v1.Topic; -import com.google.pubsub.v1.TopicName; -import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.queue.TbQueueAdmin; - -import java.io.IOException; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -@Slf4j -public class TbPubSubAdmin implements TbQueueAdmin { - private static final String ACK_DEADLINE = "ackDeadlineInSec"; - private static final String MESSAGE_RETENTION = "messageRetentionInSec"; - - private final TopicAdminClient topicAdminClient; - private final SubscriptionAdminClient subscriptionAdminClient; - - private final TbPubSubSettings pubSubSettings; - private final Set topicSet = ConcurrentHashMap.newKeySet(); - private final Set subscriptionSet = ConcurrentHashMap.newKeySet(); - private final Map subscriptionProperties; - - public TbPubSubAdmin(TbPubSubSettings pubSubSettings, Map subscriptionSettings) { - this.pubSubSettings = pubSubSettings; - this.subscriptionProperties = subscriptionSettings; - - TopicAdminSettings topicAdminSettings; - try { - topicAdminSettings = TopicAdminSettings.newBuilder().setCredentialsProvider(pubSubSettings.getCredentialsProvider()).build(); - } catch (IOException e) { - log.error("Failed to create TopicAdminSettings"); - throw new RuntimeException("Failed to create TopicAdminSettings."); - } - - SubscriptionAdminSettings subscriptionAdminSettings; - try { - subscriptionAdminSettings = SubscriptionAdminSettings.newBuilder().setCredentialsProvider(pubSubSettings.getCredentialsProvider()).build(); - } catch (IOException e) { - log.error("Failed to create SubscriptionAdminSettings"); - throw new RuntimeException("Failed to create SubscriptionAdminSettings."); - } - - try { - topicAdminClient = TopicAdminClient.create(topicAdminSettings); - - ListTopicsRequest listTopicsRequest = - ListTopicsRequest.newBuilder().setProject(ProjectName.format(pubSubSettings.getProjectId())).build(); - TopicAdminClient.ListTopicsPagedResponse response = topicAdminClient.listTopics(listTopicsRequest); - for (Topic topic : response.iterateAll()) { - topicSet.add(topic.getName()); - } - } catch (IOException e) { - log.error("Failed to get topics.", e); - throw new RuntimeException("Failed to get topics.", e); - } - - try { - subscriptionAdminClient = SubscriptionAdminClient.create(subscriptionAdminSettings); - - ListSubscriptionsRequest listSubscriptionsRequest = - ListSubscriptionsRequest.newBuilder() - .setProject(ProjectName.of(pubSubSettings.getProjectId()).toString()) - .build(); - SubscriptionAdminClient.ListSubscriptionsPagedResponse response = - subscriptionAdminClient.listSubscriptions(listSubscriptionsRequest); - - for (Subscription subscription : response.iterateAll()) { - subscriptionSet.add(subscription.getName()); - } - } catch (IOException e) { - log.error("Failed to get subscriptions.", e); - throw new RuntimeException("Failed to get subscriptions.", e); - } - } - - @Override - public void createTopicIfNotExists(String partition, String properties) { - TopicName topicName = TopicName.newBuilder() - .setTopic(partition) - .setProject(pubSubSettings.getProjectId()) - .build(); - - if (topicSet.contains(topicName.toString())) { - createSubscriptionIfNotExists(partition, topicName); - return; - } - - ListTopicsRequest listTopicsRequest = - ListTopicsRequest.newBuilder().setProject(ProjectName.format(pubSubSettings.getProjectId())).build(); - TopicAdminClient.ListTopicsPagedResponse response = topicAdminClient.listTopics(listTopicsRequest); - for (Topic topic : response.iterateAll()) { - if (topic.getName().contains(topicName.toString())) { - topicSet.add(topic.getName()); - createSubscriptionIfNotExists(partition, topicName); - return; - } - } - - try { - topicAdminClient.createTopic(topicName); - log.info("Created new topic: [{}]", topicName.toString()); - } catch (AlreadyExistsException e) { - log.info("[{}] Topic already exist.", topicName.toString()); - } finally { - topicSet.add(topicName.toString()); - } - createSubscriptionIfNotExists(partition, topicName); - } - - @Override - public void deleteTopic(String topic) { - TopicName topicName = TopicName.newBuilder() - .setTopic(topic) - .setProject(pubSubSettings.getProjectId()) - .build(); - - ProjectSubscriptionName subscriptionName = - ProjectSubscriptionName.of(pubSubSettings.getProjectId(), topic); - - if (topicSet.contains(topicName.toString())) { - topicAdminClient.deleteTopic(topicName); - } else { - if (topicAdminClient.getTopic(topicName) != null) { - topicAdminClient.deleteTopic(topicName); - } else { - log.warn("PubSub topic [{}] does not exist.", topic); - } - } - - if (subscriptionSet.contains(subscriptionName.toString())) { - subscriptionAdminClient.deleteSubscription(subscriptionName); - } else { - if (subscriptionAdminClient.getSubscription(subscriptionName) != null) { - subscriptionAdminClient.deleteSubscription(subscriptionName); - } else { - log.warn("PubSub subscription [{}] does not exist.", topic); - } - } - } - - private void createSubscriptionIfNotExists(String partition, TopicName topicName) { - ProjectSubscriptionName subscriptionName = - ProjectSubscriptionName.of(pubSubSettings.getProjectId(), partition); - - if (subscriptionSet.contains(subscriptionName.toString())) { - return; - } - - ListSubscriptionsRequest listSubscriptionsRequest = - ListSubscriptionsRequest.newBuilder().setProject(ProjectName.of(pubSubSettings.getProjectId()).toString()).build(); - SubscriptionAdminClient.ListSubscriptionsPagedResponse response = subscriptionAdminClient.listSubscriptions(listSubscriptionsRequest); - for (Subscription subscription : response.iterateAll()) { - if (subscription.getName().equals(subscriptionName.toString())) { - subscriptionSet.add(subscription.getName()); - return; - } - } - - Subscription.Builder subscriptionBuilder = Subscription - .newBuilder() - .setName(subscriptionName.toString()) - .setTopic(topicName.toString()); - - setAckDeadline(subscriptionBuilder); - setMessageRetention(subscriptionBuilder); - - try { - subscriptionAdminClient.createSubscription(subscriptionBuilder.build()); - log.info("Created new subscription: [{}]", subscriptionName.toString()); - } catch (AlreadyExistsException e) { - log.info("[{}] Subscription already exist.", subscriptionName.toString()); - } finally { - subscriptionSet.add(subscriptionName.toString()); - } - } - - private void setAckDeadline(Subscription.Builder builder) { - if (subscriptionProperties.containsKey(ACK_DEADLINE)) { - builder.setAckDeadlineSeconds(Integer.parseInt(subscriptionProperties.get(ACK_DEADLINE))); - } - } - - private void setMessageRetention(Subscription.Builder builder) { - if (subscriptionProperties.containsKey(MESSAGE_RETENTION)) { - Duration duration = Duration - .newBuilder() - .setSeconds(Long.parseLong(subscriptionProperties.get(MESSAGE_RETENTION))) - .build(); - builder.setMessageRetentionDuration(duration); - } - } - - @Override - public void destroy() { - if (topicAdminClient != null) { - topicAdminClient.close(); - } - if (subscriptionAdminClient != null) { - subscriptionAdminClient.close(); - } - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java deleted file mode 100644 index 8a2ba90096..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java +++ /dev/null @@ -1,175 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.pubsub; - -import com.google.api.core.ApiFuture; -import com.google.api.core.ApiFutures; -import com.google.cloud.pubsub.v1.stub.GrpcSubscriberStub; -import com.google.cloud.pubsub.v1.stub.SubscriberStub; -import com.google.cloud.pubsub.v1.stub.SubscriberStubSettings; -import com.google.gson.Gson; -import com.google.protobuf.InvalidProtocolBufferException; -import com.google.pubsub.v1.AcknowledgeRequest; -import com.google.pubsub.v1.ProjectSubscriptionName; -import com.google.pubsub.v1.PubsubMessage; -import com.google.pubsub.v1.PullRequest; -import com.google.pubsub.v1.PullResponse; -import com.google.pubsub.v1.ReceivedMessage; -import lombok.extern.slf4j.Slf4j; -import org.springframework.util.CollectionUtils; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueMsg; -import org.thingsboard.server.queue.TbQueueMsgDecoder; -import org.thingsboard.server.queue.common.AbstractParallelTbQueueConsumerTemplate; -import org.thingsboard.server.queue.common.DefaultTbQueueMsg; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.ExecutionException; -import java.util.stream.Collectors; - -@Slf4j -public class TbPubSubConsumerTemplate extends AbstractParallelTbQueueConsumerTemplate { - - private final Gson gson = new Gson(); - private final TbQueueAdmin admin; - private final String topic; - private final TbQueueMsgDecoder decoder; - private final TbPubSubSettings pubSubSettings; - - private volatile Set subscriptionNames; - private final List acknowledgeRequests = new CopyOnWriteArrayList<>(); - - private final SubscriberStub subscriber; - private volatile int messagesPerTopic; - - public TbPubSubConsumerTemplate(TbQueueAdmin admin, TbPubSubSettings pubSubSettings, String topic, TbQueueMsgDecoder decoder) { - super(topic); - this.admin = admin; - this.pubSubSettings = pubSubSettings; - this.topic = topic; - this.decoder = decoder; - try { - SubscriberStubSettings subscriberStubSettings = - SubscriberStubSettings.newBuilder() - .setCredentialsProvider(pubSubSettings.getCredentialsProvider()) - .setTransportChannelProvider( - SubscriberStubSettings.defaultGrpcTransportProviderBuilder() - .setMaxInboundMessageSize(pubSubSettings.getMaxMsgSize()) - .build()) - .setExecutorProvider(pubSubSettings.getExecutorProvider()) - .build(); - this.subscriber = GrpcSubscriberStub.create(subscriberStubSettings); - } catch (IOException e) { - log.error("Failed to create subscriber.", e); - throw new RuntimeException("Failed to create subscriber.", e); - } - } - - @Override - protected List doPoll(long durationInMillis) { - try { - List messages = receiveMessages(); - if (!messages.isEmpty()) { - return messages.stream().map(ReceivedMessage::getMessage).collect(Collectors.toList()); - } - } catch (ExecutionException | InterruptedException e) { - if (stopped) { - log.info("[{}] Pub/Sub consumer is stopped.", topic); - } else { - log.error("Failed to receive messages", e); - } - } - return Collections.emptyList(); - } - - @Override - protected void doSubscribe(List topicNames) { - subscriptionNames = new LinkedHashSet<>(topicNames); - subscriptionNames.forEach(admin::createTopicIfNotExists); - initNewExecutor(subscriptionNames.size() + 1); - messagesPerTopic = pubSubSettings.getMaxMessages() / Math.max(subscriptionNames.size(), 1); - } - - @Override - protected void doCommit() { - acknowledgeRequests.forEach(subscriber.acknowledgeCallable()::futureCall); - acknowledgeRequests.clear(); - } - - @Override - protected void doUnsubscribe() { - if (subscriber != null) { - subscriber.close(); - } - shutdownExecutor(); - } - - private List receiveMessages() throws ExecutionException, InterruptedException { - List>> result = subscriptionNames.stream().map(subscriptionId -> { - String subscriptionName = ProjectSubscriptionName.format(pubSubSettings.getProjectId(), subscriptionId); - PullRequest pullRequest = - PullRequest.newBuilder() - .setMaxMessages(messagesPerTopic) -// .setReturnImmediately(false) // return immediately if messages are not available - .setSubscription(subscriptionName) - .build(); - - ApiFuture pullResponseApiFuture = subscriber.pullCallable().futureCall(pullRequest); - - return ApiFutures.transform(pullResponseApiFuture, pullResponse -> { - if (pullResponse != null && !pullResponse.getReceivedMessagesList().isEmpty()) { - List ackIds = new ArrayList<>(); - for (ReceivedMessage message : pullResponse.getReceivedMessagesList()) { - ackIds.add(message.getAckId()); - } - AcknowledgeRequest acknowledgeRequest = - AcknowledgeRequest.newBuilder() - .setSubscription(subscriptionName) - .addAllAckIds(ackIds) - .build(); - - acknowledgeRequests.add(acknowledgeRequest); - return pullResponse.getReceivedMessagesList(); - } - return null; - }, consumerExecutor); - - }).collect(Collectors.toList()); - - ApiFuture> transform = ApiFutures.transform(ApiFutures.allAsList(result), listMessages -> { - if (!CollectionUtils.isEmpty(listMessages)) { - return listMessages.stream().filter(Objects::nonNull).flatMap(List::stream).collect(Collectors.toList()); - } - return Collections.emptyList(); - }, consumerExecutor); - - return transform.get(); - } - - @Override - public T decode(PubsubMessage message) throws InvalidProtocolBufferException { - DefaultTbQueueMsg msg = gson.fromJson(message.getData().toStringUtf8(), DefaultTbQueueMsg.class); - return decoder.decode(msg); - } - -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java deleted file mode 100644 index bb1d4e8976..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java +++ /dev/null @@ -1,137 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.pubsub; - -import com.google.api.core.ApiFuture; -import com.google.api.core.ApiFutureCallback; -import com.google.api.core.ApiFutures; -import com.google.cloud.pubsub.v1.Publisher; -import com.google.gson.Gson; -import com.google.protobuf.ByteString; -import com.google.pubsub.v1.ProjectTopicName; -import com.google.pubsub.v1.PubsubMessage; -import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueCallback; -import org.thingsboard.server.queue.TbQueueMsg; -import org.thingsboard.server.queue.TbQueueProducer; -import org.thingsboard.server.queue.common.DefaultTbQueueMsg; - -import java.io.IOException; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -@Slf4j -public class TbPubSubProducerTemplate implements TbQueueProducer { - - private final Gson gson = new Gson(); - - private final String defaultTopic; - private final TbQueueAdmin admin; - private final TbPubSubSettings pubSubSettings; - - private final Map publisherMap = new ConcurrentHashMap<>(); - - private final ExecutorService pubExecutor = Executors.newCachedThreadPool(); - - public TbPubSubProducerTemplate(TbQueueAdmin admin, TbPubSubSettings pubSubSettings, String defaultTopic) { - this.defaultTopic = defaultTopic; - this.admin = admin; - this.pubSubSettings = pubSubSettings; - } - - @Override - public void init() { - - } - - @Override - public String getDefaultTopic() { - return defaultTopic; - } - - @Override - public void send(TopicPartitionInfo tpi, T msg, TbQueueCallback callback) { - PubsubMessage.Builder pubsubMessageBuilder = PubsubMessage.newBuilder(); - pubsubMessageBuilder.setData(getMsg(msg)); - - Publisher publisher = getOrCreatePublisher(tpi.getFullTopicName()); - ApiFuture future = publisher.publish(pubsubMessageBuilder.build()); - - ApiFutures.addCallback(future, new ApiFutureCallback() { - public void onSuccess(String messageId) { - if (callback != null) { - callback.onSuccess(null); - } - } - - public void onFailure(Throwable t) { - if (callback != null) { - callback.onFailure(t); - } - } - }, pubExecutor); - } - - @Override - public void stop() { - publisherMap.forEach((k, v) -> { - if (v != null) { - try { - v.shutdown(); - v.awaitTermination(1, TimeUnit.SECONDS); - } catch (Exception e) { - log.error("Failed to shutdown PubSub client during destroy()", e); - } - } - }); - - if (pubExecutor != null) { - pubExecutor.shutdownNow(); - } - } - - private ByteString getMsg(T msg) { - String json = gson.toJson(new DefaultTbQueueMsg(msg)); - return ByteString.copyFrom(json.getBytes()); - } - - private Publisher getOrCreatePublisher(String topic) { - if (publisherMap.containsKey(topic)) { - return publisherMap.get(topic); - } else { - try { - admin.createTopicIfNotExists(topic); - ProjectTopicName topicName = ProjectTopicName.of(pubSubSettings.getProjectId(), topic); - Publisher publisher = Publisher.newBuilder(topicName) - .setCredentialsProvider(pubSubSettings.getCredentialsProvider()) - .setExecutorProvider(pubSubSettings.getExecutorProvider()) - .build(); - publisherMap.put(topic, publisher); - return publisher; - } catch (IOException e) { - log.error("Failed to create Publisher for the topic [{}].", topic, e); - throw new RuntimeException("Failed to create Publisher for the topic.", e); - } - } - - } - -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSettings.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSettings.java deleted file mode 100644 index a16d3d275f..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSettings.java +++ /dev/null @@ -1,83 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.pubsub; - -import com.google.api.gax.core.CredentialsProvider; -import com.google.api.gax.core.FixedCredentialsProvider; -import com.google.api.gax.core.FixedExecutorProvider; -import com.google.auth.oauth2.ServiceAccountCredentials; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; -import lombok.Data; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.stereotype.Component; -import org.thingsboard.common.util.ThingsBoardThreadFactory; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.concurrent.Executors; - -@Slf4j -@ConditionalOnExpression("'${queue.type:null}'=='pubsub'") -@Component -@Data -public class TbPubSubSettings { - - @Value("${queue.pubsub.project_id}") - private String projectId; - - @Value("${queue.pubsub.service_account}") - private String serviceAccount; - - @Value("${queue.pubsub.max_msg_size}") - private int maxMsgSize; - - @Value("${queue.pubsub.max_messages}") - private int maxMessages; - - @Value("${queue.pubsub.executor_thread_pool_size:0}") - private int threadPoolSize; - - /** - * Refers to com.google.cloud.pubsub.v1.Publisher default executor configuration - */ - private static final int THREADS_PER_CPU = 5; - - private FixedExecutorProvider executorProvider; - - private CredentialsProvider credentialsProvider; - - @PostConstruct - private void init() throws IOException { - ServiceAccountCredentials credentials = ServiceAccountCredentials.fromStream( - new ByteArrayInputStream(serviceAccount.getBytes())); - credentialsProvider = FixedCredentialsProvider.create(credentials); - if (threadPoolSize == 0) { - threadPoolSize = THREADS_PER_CPU * Runtime.getRuntime().availableProcessors(); - } - executorProvider = FixedExecutorProvider - .create(Executors.newScheduledThreadPool(threadPoolSize, ThingsBoardThreadFactory.forName("pubsub-queue-executor"))); - } - - @PreDestroy - private void destroy() { - if (executorProvider != null) { - executorProvider.getExecutor().shutdownNow(); - } - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSubscriptionSettings.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSubscriptionSettings.java deleted file mode 100644 index 14aa67a4f0..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSubscriptionSettings.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.pubsub; - -import jakarta.annotation.PostConstruct; -import lombok.Getter; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.stereotype.Component; -import org.thingsboard.server.queue.util.PropertyUtils; - -import java.util.Map; - -@Component -@ConditionalOnExpression("'${queue.type:null}'=='pubsub'") -public class TbPubSubSubscriptionSettings { - - @Value("${queue.pubsub.queue-properties.core:}") - private String coreProperties; - @Value("${queue.pubsub.queue-properties.rule-engine:}") - private String ruleEngineProperties; - @Value("${queue.pubsub.queue-properties.transport-api:}") - private String transportApiProperties; - @Value("${queue.pubsub.queue-properties.notifications:}") - private String notificationsProperties; - @Value("${queue.pubsub.queue-properties.js-executor:}") - private String jsExecutorProperties; - @Value("${queue.pubsub.queue-properties.version-control:}") - private String vcProperties; - @Value("${queue.pubsub.queue-properties.edge:}") - private String edgeProperties; - - @Getter - private Map coreSettings; - @Getter - private Map ruleEngineSettings; - @Getter - private Map transportApiSettings; - @Getter - private Map notificationsSettings; - @Getter - private Map jsExecutorSettings; - @Getter - private Map vcSettings; - @Getter - private Map edgeSettings; - - @PostConstruct - private void init() { - coreSettings = PropertyUtils.getProps(coreProperties); - ruleEngineSettings = PropertyUtils.getProps(ruleEngineProperties); - transportApiSettings = PropertyUtils.getProps(transportApiProperties); - notificationsSettings = PropertyUtils.getProps(notificationsProperties); - jsExecutorSettings = PropertyUtils.getProps(jsExecutorProperties); - vcSettings = PropertyUtils.getProps(vcProperties); - edgeSettings = PropertyUtils.getProps(edgeProperties); - } - -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqAdmin.java b/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqAdmin.java deleted file mode 100644 index 2d0cd5b414..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqAdmin.java +++ /dev/null @@ -1,94 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.rabbitmq; - -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.thingsboard.server.queue.TbQueueAdmin; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeoutException; - -@Slf4j -public class TbRabbitMqAdmin implements TbQueueAdmin { - - private final Channel channel; - private final Connection connection; - private final Map arguments; - - public TbRabbitMqAdmin(TbRabbitMqSettings rabbitMqSettings, Map arguments) { - this.arguments = arguments; - - try { - connection = rabbitMqSettings.getConnectionFactory().newConnection(); - } catch (IOException | TimeoutException e) { - log.error("Failed to create connection.", e); - throw new RuntimeException("Failed to create connection.", e); - } - - try { - channel = connection.createChannel(); - } catch (IOException e) { - log.error("Failed to create chanel.", e); - throw new RuntimeException("Failed to create chanel.", e); - } - } - - @Override - public void createTopicIfNotExists(String topic, String properties) { - Map arguments = this.arguments; - if (StringUtils.isNotBlank(properties)) { - arguments = new HashMap<>(arguments); - arguments.putAll(TbRabbitMqQueueArguments.getArgs(properties)); - } - try { - channel.queueDeclare(topic, false, false, false, arguments); - } catch (IOException e) { - log.error("Failed to bind queue: [{}]", topic, e); - } - } - - @Override - public void deleteTopic(String topic) { - try { - channel.queueDelete(topic); - } catch (IOException e) { - log.error("Failed to delete RabbitMq queue [{}].", topic); - } - } - - @Override - public void destroy() { - if (channel != null) { - try { - channel.close(); - } catch (IOException | TimeoutException e) { - log.error("Failed to close Chanel.", e); - } - } - if (connection != null) { - try { - connection.close(); - } catch (IOException e) { - log.error("Failed to close Connection.", e); - } - } - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqConsumerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqConsumerTemplate.java deleted file mode 100644 index e50afabb5d..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqConsumerTemplate.java +++ /dev/null @@ -1,144 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.rabbitmq; - -import com.google.gson.Gson; -import com.google.protobuf.InvalidProtocolBufferException; -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; -import com.rabbitmq.client.GetResponse; -import java.util.ArrayList; -import java.util.Collection; -import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueMsg; -import org.thingsboard.server.queue.TbQueueMsgDecoder; -import org.thingsboard.server.queue.common.AbstractTbQueueConsumerTemplate; -import org.thingsboard.server.queue.common.DefaultTbQueueMsg; - -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.concurrent.TimeoutException; -import java.util.stream.Collectors; - -@Slf4j -public class TbRabbitMqConsumerTemplate extends AbstractTbQueueConsumerTemplate { - - private final Gson gson = new Gson(); - private final TbQueueAdmin admin; - private final TbQueueMsgDecoder decoder; - private final Channel channel; - private final Connection connection; - private final int maxPollMessages; - - private volatile Set queues; - - public TbRabbitMqConsumerTemplate(TbQueueAdmin admin, TbRabbitMqSettings rabbitMqSettings, String topic, TbQueueMsgDecoder decoder) { - super(topic); - this.admin = admin; - this.decoder = decoder; - this.maxPollMessages = rabbitMqSettings.getMaxPollMessages(); - try { - connection = rabbitMqSettings.getConnectionFactory().newConnection(); - } catch (IOException | TimeoutException e) { - log.error("Failed to create connection.", e); - throw new RuntimeException("Failed to create connection.", e); - } - try { - channel = connection.createChannel(); - } catch (IOException e) { - log.error("Failed to create chanel.", e); - throw new RuntimeException("Failed to create chanel.", e); - } - stopped = false; - } - - @Override - protected List doPoll(long durationInMillis) { - List result = queues.stream() - .map(queue -> { - List messages = new ArrayList<>(); - for (int i = 0; i < maxPollMessages; i++) { - GetResponse response = doQueuePoll(queue); - if (response == null) { - break; - } - messages.add(response); - } - return messages; - }) - .filter(r -> !r.isEmpty()) - .flatMap(Collection::stream) - .collect(Collectors.toList()); - if (result.size() > 0) { - return result; - } else { - return Collections.emptyList(); - } - } - - protected GetResponse doQueuePoll(String queue) { - try { - return channel.basicGet(queue, false); - } catch (IOException e) { - log.error("Failed to get messages from queue: [{}]", queue); - throw new RuntimeException("Failed to get messages from queue.", e); - } - } - - @Override - protected void doSubscribe(List topicNames) { - queues = partitions.stream() - .map(TopicPartitionInfo::getFullTopicName) - .collect(Collectors.toSet()); - queues.forEach(admin::createTopicIfNotExists); - } - - @Override - protected void doCommit() { - try { - channel.basicAck(0, true); - } catch (IOException e) { - log.error("Failed to ack messages.", e); - } - } - - @Override - protected void doUnsubscribe() { - if (channel != null) { - try { - channel.close(); - } catch (IOException | TimeoutException e) { - log.error("Failed to close the channel."); - } - } - if (connection != null) { - try { - connection.close(); - } catch (IOException e) { - log.error("Failed to close the connection."); - } - } - } - - public T decode(GetResponse message) throws InvalidProtocolBufferException { - DefaultTbQueueMsg msg = gson.fromJson(new String(message.getBody()), DefaultTbQueueMsg.class); - return decoder.decode(msg); - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqProducerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqProducerTemplate.java deleted file mode 100644 index 696fcccce2..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqProducerTemplate.java +++ /dev/null @@ -1,125 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.rabbitmq; - -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; -import com.google.gson.Gson; -import com.rabbitmq.client.AMQP; -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; -import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueCallback; -import org.thingsboard.server.queue.TbQueueMsg; -import org.thingsboard.server.queue.TbQueueProducer; -import org.thingsboard.server.queue.common.DefaultTbQueueMsg; - -import java.io.IOException; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeoutException; - -@Slf4j -public class TbRabbitMqProducerTemplate implements TbQueueProducer { - private final String defaultTopic; - private final Gson gson = new Gson(); - private final TbQueueAdmin admin; - private final TbRabbitMqSettings rabbitMqSettings; - private final ListeningExecutorService producerExecutor; - private final Channel channel; - private final Connection connection; - - private final Set topics = ConcurrentHashMap.newKeySet(); - - public TbRabbitMqProducerTemplate(TbQueueAdmin admin, TbRabbitMqSettings rabbitMqSettings, String defaultTopic) { - this.admin = admin; - this.defaultTopic = defaultTopic; - this.rabbitMqSettings = rabbitMqSettings; - producerExecutor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool()); - try { - connection = rabbitMqSettings.getConnectionFactory().newConnection(); - } catch (IOException | TimeoutException e) { - log.error("Failed to create connection.", e); - throw new RuntimeException("Failed to create connection.", e); - } - - try { - channel = connection.createChannel(); - } catch (IOException e) { - log.error("Failed to create chanel.", e); - throw new RuntimeException("Failed to create chanel.", e); - } - } - - @Override - public void init() { - - } - - @Override - public String getDefaultTopic() { - return defaultTopic; - } - - @Override - public void send(TopicPartitionInfo tpi, T msg, TbQueueCallback callback) { - createTopicIfNotExist(tpi); - AMQP.BasicProperties properties = new AMQP.BasicProperties(); - try { - channel.basicPublish(rabbitMqSettings.getExchangeName(), tpi.getFullTopicName(), properties, gson.toJson(new DefaultTbQueueMsg(msg)).getBytes()); - if (callback != null) { - callback.onSuccess(null); - } - } catch (IOException e) { - log.error("Failed publish message: [{}].", msg, e); - if (callback != null) { - callback.onFailure(e); - } - } - } - - @Override - public void stop() { - if (producerExecutor != null) { - producerExecutor.shutdownNow(); - } - if (channel != null) { - try { - channel.close(); - } catch (IOException | TimeoutException e) { - log.error("Failed to close the channel."); - } - } - if (connection != null) { - try { - connection.close(); - } catch (IOException e) { - log.error("Failed to close the connection."); - } - } - } - - private void createTopicIfNotExist(TopicPartitionInfo tpi) { - if (topics.contains(tpi)) { - return; - } - admin.createTopicIfNotExists(tpi.getFullTopicName()); - topics.add(tpi); - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqQueueArguments.java b/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqQueueArguments.java deleted file mode 100644 index 06613d5617..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqQueueArguments.java +++ /dev/null @@ -1,111 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.rabbitmq; - -import jakarta.annotation.PostConstruct; -import lombok.Getter; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.StringUtils; - -import java.util.HashMap; -import java.util.Map; -import java.util.regex.Pattern; - -@Component -@ConditionalOnExpression("'${queue.type:null}'=='rabbitmq'") -public class TbRabbitMqQueueArguments { - @Value("${queue.rabbitmq.queue-properties.core:}") - private String coreProperties; - @Value("${queue.rabbitmq.queue-properties.rule-engine:}") - private String ruleEngineProperties; - @Value("${queue.rabbitmq.queue-properties.transport-api:}") - private String transportApiProperties; - @Value("${queue.rabbitmq.queue-properties.notifications:}") - private String notificationsProperties; - @Value("${queue.rabbitmq.queue-properties.js-executor:}") - private String jsExecutorProperties; - @Value("${queue.rabbitmq.queue-properties.version-control:}") - private String vcProperties; - @Value("${queue.rabbitmq.queue-properties.edge:}") - private String edgeProperties; - - @Getter - private Map coreArgs; - @Getter - private Map ruleEngineArgs; - @Getter - private Map transportApiArgs; - @Getter - private Map notificationsArgs; - @Getter - private Map jsExecutorArgs; - @Getter - private Map vcArgs; - @Getter - private Map edgeArgs; - - @PostConstruct - private void init() { - coreArgs = getArgs(coreProperties); - ruleEngineArgs = getArgs(ruleEngineProperties); - transportApiArgs = getArgs(transportApiProperties); - notificationsArgs = getArgs(notificationsProperties); - jsExecutorArgs = getArgs(jsExecutorProperties); - vcArgs = getArgs(vcProperties); - edgeArgs = getArgs(edgeProperties); - } - - public static Map getArgs(String properties) { - Map configs = new HashMap<>(); - if (StringUtils.isNotEmpty(properties)) { - for (String property : properties.split(";")) { - int delimiterPosition = property.indexOf(":"); - String key = property.substring(0, delimiterPosition); - String strValue = property.substring(delimiterPosition + 1); - configs.put(key, getObjectValue(strValue)); - } - } - return configs; - } - - private static Object getObjectValue(String str) { - if (str.equalsIgnoreCase("true") || str.equalsIgnoreCase("false")) { - return Boolean.valueOf(str); - } else if (isNumeric(str)) { - return getNumericValue(str); - } - return str; - } - - private static Object getNumericValue(String str) { - if (str.contains(".")) { - return Double.valueOf(str); - } else { - return Long.valueOf(str); - } - } - - private static final Pattern PATTERN = Pattern.compile("-?\\d+(\\.\\d+)?"); - - private static boolean isNumeric(String strNum) { - if (strNum == null) { - return false; - } - return PATTERN.matcher(strNum).matches(); - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqSettings.java b/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqSettings.java deleted file mode 100644 index c0a2912f23..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqSettings.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.rabbitmq; - -import com.rabbitmq.client.ConnectionFactory; -import jakarta.annotation.PostConstruct; -import lombok.Data; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.stereotype.Component; - -@Slf4j -@ConditionalOnExpression("'${queue.type:null}'=='rabbitmq'") -@Component -@Data -public class TbRabbitMqSettings { - @Value("${queue.rabbitmq.exchange_name:}") - private String exchangeName; - @Value("${queue.rabbitmq.host:}") - private String host; - @Value("${queue.rabbitmq.port:}") - private int port; - @Value("${queue.rabbitmq.virtual_host:}") - private String virtualHost; - @Value("${queue.rabbitmq.username:}") - private String username; - @Value("${queue.rabbitmq.password:}") - private String password; - @Value("${queue.rabbitmq.automatic_recovery_enabled:}") - private boolean automaticRecoveryEnabled; - @Value("${queue.rabbitmq.connection_timeout:}") - private int connectionTimeout; - @Value("${queue.rabbitmq.handshake_timeout:}") - private int handshakeTimeout; - @Value("${queue.rabbitmq.max_poll_messages:1}") - private int maxPollMessages; - - private ConnectionFactory connectionFactory; - - @PostConstruct - private void init() { - connectionFactory = new ConnectionFactory(); - connectionFactory.setHost(host); - connectionFactory.setPort(port); - connectionFactory.setVirtualHost(virtualHost); - connectionFactory.setUsername(username); - connectionFactory.setPassword(password); - connectionFactory.setAutomaticRecoveryEnabled(automaticRecoveryEnabled); - connectionFactory.setConnectionTimeout(connectionTimeout); - connectionFactory.setHandshakeTimeout(handshakeTimeout); - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/sqs/AwsSqsTbQueueMsgMetadata.java b/common/queue/src/main/java/org/thingsboard/server/queue/sqs/AwsSqsTbQueueMsgMetadata.java deleted file mode 100644 index 8d13a543d3..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/sqs/AwsSqsTbQueueMsgMetadata.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.sqs; - -import com.amazonaws.http.SdkHttpMetadata; -import lombok.AllArgsConstructor; -import lombok.Data; -import org.thingsboard.server.queue.TbQueueMsgMetadata; - -@Data -@AllArgsConstructor -public class AwsSqsTbQueueMsgMetadata implements TbQueueMsgMetadata { - - private final SdkHttpMetadata metadata; -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsAdmin.java b/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsAdmin.java deleted file mode 100644 index 92def925d7..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsAdmin.java +++ /dev/null @@ -1,117 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.sqs; - -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.auth.AWSCredentialsProvider; -import com.amazonaws.auth.AWSStaticCredentialsProvider; -import com.amazonaws.auth.BasicAWSCredentials; -import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; -import com.amazonaws.services.sqs.AmazonSQS; -import com.amazonaws.services.sqs.AmazonSQSClientBuilder; -import com.amazonaws.services.sqs.model.CreateQueueRequest; -import com.amazonaws.services.sqs.model.GetQueueUrlResult; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import org.thingsboard.common.util.ThingsBoardExecutors; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.util.PropertyUtils; - -import java.util.Map; -import java.util.concurrent.ExecutorService; -import java.util.function.Function; -import java.util.stream.Collectors; - -@Slf4j -public class TbAwsSqsAdmin implements TbQueueAdmin { - - private final Map attributes; - private final AmazonSQS sqsClient; - private final Map queues; - @Getter - private final ExecutorService producerExecutor; - - public TbAwsSqsAdmin(TbAwsSqsSettings sqsSettings, Map attributes) { - this.attributes = attributes; - - AWSCredentialsProvider credentialsProvider; - if (sqsSettings.getUseDefaultCredentialProviderChain()) { - credentialsProvider = new DefaultAWSCredentialsProviderChain(); - } else { - AWSCredentials awsCredentials = new BasicAWSCredentials(sqsSettings.getAccessKeyId(), sqsSettings.getSecretAccessKey()); - credentialsProvider = new AWSStaticCredentialsProvider(awsCredentials); - } - producerExecutor = ThingsBoardExecutors.newWorkStealingPool(sqsSettings.getThreadPoolSize(), "aws-sqs-queue-executor"); - - sqsClient = AmazonSQSClientBuilder.standard() - .withCredentials(credentialsProvider) - .withRegion(sqsSettings.getRegion()) - .build(); - - queues = sqsClient - .listQueues() - .getQueueUrls() - .stream() - .map(this::getQueueNameFromUrl) - .collect(Collectors.toMap(this::convertTopicToQueueName, Function.identity())); - } - - @Override - public void createTopicIfNotExists(String topic, String properties) { - String queueName = convertTopicToQueueName(topic); - if (queues.containsKey(queueName)) { - return; - } - Map attributes = PropertyUtils.getProps(this.attributes, properties, TbAwsSqsQueueAttributes::toConfigs); - final CreateQueueRequest createQueueRequest = new CreateQueueRequest(queueName).withAttributes(attributes); - String queueUrl = sqsClient.createQueue(createQueueRequest).getQueueUrl(); - queues.put(getQueueNameFromUrl(queueUrl), queueUrl); - } - - private String convertTopicToQueueName(String topic) { - return topic.replaceAll("\\.", "_") + ".fifo"; - } - - @Override - public void deleteTopic(String topic) { - String queueName = convertTopicToQueueName(topic); - if (queues.containsKey(queueName)) { - sqsClient.deleteQueue(queues.get(queueName)); - } else { - GetQueueUrlResult queueUrl = sqsClient.getQueueUrl(queueName); - if (queueUrl != null) { - sqsClient.deleteQueue(queueUrl.getQueueUrl()); - } else { - log.warn("Aws SQS queue [{}] does not exist!", queueName); - } - } - } - - private String getQueueNameFromUrl(String queueUrl) { - int delimiterIndex = queueUrl.lastIndexOf("/"); - return queueUrl.substring(delimiterIndex + 1); - } - - @Override - public void destroy() { - if (sqsClient != null) { - sqsClient.shutdown(); - } - if (producerExecutor != null) { - producerExecutor.shutdownNow(); - } - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsConsumerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsConsumerTemplate.java deleted file mode 100644 index a3087e9d11..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsConsumerTemplate.java +++ /dev/null @@ -1,188 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.sqs; - -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.auth.AWSCredentialsProvider; -import com.amazonaws.auth.AWSStaticCredentialsProvider; -import com.amazonaws.auth.BasicAWSCredentials; -import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; -import com.amazonaws.services.sqs.AmazonSQS; -import com.amazonaws.services.sqs.AmazonSQSClientBuilder; -import com.amazonaws.services.sqs.model.DeleteMessageBatchRequestEntry; -import com.amazonaws.services.sqs.model.Message; -import com.amazonaws.services.sqs.model.ReceiveMessageRequest; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.gson.Gson; -import com.google.protobuf.InvalidProtocolBufferException; -import lombok.Data; -import lombok.extern.slf4j.Slf4j; -import org.springframework.util.CollectionUtils; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueMsg; -import org.thingsboard.server.queue.TbQueueMsgDecoder; -import org.thingsboard.server.queue.common.AbstractParallelTbQueueConsumerTemplate; -import org.thingsboard.server.queue.common.DefaultTbQueueMsg; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -@Slf4j -public class TbAwsSqsConsumerTemplate extends AbstractParallelTbQueueConsumerTemplate { - - private static final int MAX_NUM_MSGS = 10; - - private final Gson gson = new Gson(); - private final TbQueueAdmin admin; - private final AmazonSQS sqsClient; - private final TbQueueMsgDecoder decoder; - private final TbAwsSqsSettings sqsSettings; - - private final List pendingMessages = new CopyOnWriteArrayList<>(); - private volatile Set queueUrls; - - public TbAwsSqsConsumerTemplate(TbQueueAdmin admin, TbAwsSqsSettings sqsSettings, String topic, TbQueueMsgDecoder decoder) { - super(topic); - this.admin = admin; - this.decoder = decoder; - this.sqsSettings = sqsSettings; - - AWSCredentialsProvider credentialsProvider; - if (sqsSettings.getUseDefaultCredentialProviderChain()) { - credentialsProvider = new DefaultAWSCredentialsProviderChain(); - } else { - AWSCredentials awsCredentials = new BasicAWSCredentials(sqsSettings.getAccessKeyId(), sqsSettings.getSecretAccessKey()); - credentialsProvider = new AWSStaticCredentialsProvider(awsCredentials); - } - - sqsClient = AmazonSQSClientBuilder.standard() - .withCredentials(credentialsProvider) - .withRegion(sqsSettings.getRegion()) - .build(); - - } - - @Override - protected void doSubscribe(List topicNames) { - queueUrls = topicNames.stream().map(this::getQueueUrl).collect(Collectors.toSet()); - initNewExecutor(queueUrls.size() * sqsSettings.getThreadsPerTopic() + 1); - } - - @Override - protected List doPoll(long durationInMillis) { - int duration = (int) TimeUnit.MILLISECONDS.toSeconds(durationInMillis); - List>> futureList = queueUrls - .stream() - .map(url -> poll(url, duration)) - .collect(Collectors.toList()); - ListenableFuture>> futureResult = Futures.allAsList(futureList); - try { - return futureResult.get().stream() - .flatMap(List::stream) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - } catch (InterruptedException | ExecutionException e) { - if (stopped) { - log.info("[{}] Aws SQS consumer is stopped.", getTopic()); - } else { - log.error("Failed to pool messages.", e); - } - return Collections.emptyList(); - } - } - - @Override - public T decode(Message message) throws InvalidProtocolBufferException { - DefaultTbQueueMsg msg = gson.fromJson(message.getBody(), DefaultTbQueueMsg.class); - return decoder.decode(msg); - } - - @Override - protected void doCommit() { - pendingMessages.forEach(msg -> - consumerExecutor.submit(() -> { - List entries = msg.getMessages() - .stream() - .map(message -> new DeleteMessageBatchRequestEntry(message.getMessageId(), message.getReceiptHandle())) - .collect(Collectors.toList()); - sqsClient.deleteMessageBatch(msg.getUrl(), entries); - })); - pendingMessages.clear(); - } - - @Override - protected void doUnsubscribe() { - stopped = true; - if (sqsClient != null) { - sqsClient.shutdown(); - } - shutdownExecutor(); - } - - private ListenableFuture> poll(String url, int waitTimeSeconds) { - List>> result = new ArrayList<>(); - - for (int i = 0; i < sqsSettings.getThreadsPerTopic(); i++) { - result.add(consumerExecutor.submit(() -> { - ReceiveMessageRequest request = new ReceiveMessageRequest(); - request - .withWaitTimeSeconds(waitTimeSeconds) - .withQueueUrl(url) - .withMaxNumberOfMessages(MAX_NUM_MSGS); - return sqsClient.receiveMessage(request).getMessages(); - })); - } - return Futures.transform(Futures.allAsList(result), list -> { - if (!CollectionUtils.isEmpty(list)) { - return list.stream() - .flatMap(messageList -> { - if (!messageList.isEmpty()) { - this.pendingMessages.add(new AwsSqsMsgWrapper(url, messageList)); - return messageList.stream(); - } - return Stream.empty(); - }) - .collect(Collectors.toList()); - } - return Collections.emptyList(); - }, consumerExecutor); - } - - @Data - private static class AwsSqsMsgWrapper { - private final String url; - private final List messages; - - public AwsSqsMsgWrapper(String url, List messages) { - this.url = url; - this.messages = messages; - } - } - - private String getQueueUrl(String topic) { - admin.createTopicIfNotExists(topic); - return sqsClient.getQueueUrl(topic.replaceAll("\\.", "_") + ".fifo").getQueueUrl(); - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsProducerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsProducerTemplate.java deleted file mode 100644 index d8d4a5d2e8..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsProducerTemplate.java +++ /dev/null @@ -1,117 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.sqs; - -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.auth.AWSCredentialsProvider; -import com.amazonaws.auth.AWSStaticCredentialsProvider; -import com.amazonaws.auth.BasicAWSCredentials; -import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; -import com.amazonaws.handlers.AsyncHandler; -import com.amazonaws.services.sqs.AmazonSQSAsync; -import com.amazonaws.services.sqs.AmazonSQSAsyncClientBuilder; -import com.amazonaws.services.sqs.model.SendMessageRequest; -import com.amazonaws.services.sqs.model.SendMessageResult; -import com.google.gson.Gson; -import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueCallback; -import org.thingsboard.server.queue.TbQueueMsg; -import org.thingsboard.server.queue.TbQueueProducer; -import org.thingsboard.server.queue.common.DefaultTbQueueMsg; - -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; - -@Slf4j -public class TbAwsSqsProducerTemplate implements TbQueueProducer { - private final String defaultTopic; - private final AmazonSQSAsync sqsClient; - private final Gson gson = new Gson(); - private final Map queueUrlMap = new ConcurrentHashMap<>(); - private final TbAwsSqsAdmin admin; - - public TbAwsSqsProducerTemplate(TbQueueAdmin admin, TbAwsSqsSettings sqsSettings, String defaultTopic) { - this.admin = (TbAwsSqsAdmin) admin; - this.defaultTopic = defaultTopic; - - AWSCredentialsProvider credentialsProvider; - if (sqsSettings.getUseDefaultCredentialProviderChain()) { - credentialsProvider = new DefaultAWSCredentialsProviderChain(); - } else { - AWSCredentials awsCredentials = new BasicAWSCredentials(sqsSettings.getAccessKeyId(), sqsSettings.getSecretAccessKey()); - credentialsProvider = new AWSStaticCredentialsProvider(awsCredentials); - } - - sqsClient = AmazonSQSAsyncClientBuilder.standard() - .withCredentials(credentialsProvider) - .withRegion(sqsSettings.getRegion()) - .withExecutorFactory(this.admin::getProducerExecutor) - .build(); - } - - @Override - public void init() { - - } - - @Override - public String getDefaultTopic() { - return defaultTopic; - } - - @Override - public void send(TopicPartitionInfo tpi, T msg, TbQueueCallback callback) { - SendMessageRequest sendMsgRequest = new SendMessageRequest(); - sendMsgRequest.withQueueUrl(getQueueUrl(tpi.getFullTopicName())); - sendMsgRequest.withMessageBody(gson.toJson(new DefaultTbQueueMsg(msg))); - - String sqsMsgId = UUID.randomUUID().toString(); - sendMsgRequest.withMessageGroupId(sqsMsgId); - sendMsgRequest.withMessageDeduplicationId(sqsMsgId); - - sqsClient.sendMessageAsync(sendMsgRequest, new AsyncHandler() { - @Override public void onError(Exception e) { - if (callback != null) { - callback.onFailure(e); - } - } - - @Override public void onSuccess(SendMessageRequest request, - SendMessageResult sendMessageResult) { - if (callback != null) { - callback.onSuccess(new AwsSqsTbQueueMsgMetadata(sendMessageResult.getSdkHttpMetadata())); - } - } - }); - } - - @Override - public void stop() { - if (sqsClient != null) { - sqsClient.shutdown(); - } - } - - private String getQueueUrl(String topic) { - return queueUrlMap.computeIfAbsent(topic, k -> { - admin.createTopicIfNotExists(topic); - return sqsClient.getQueueUrl(topic.replaceAll("\\.", "_") + ".fifo").getQueueUrl(); - }); - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsQueueAttributes.java b/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsQueueAttributes.java deleted file mode 100644 index 2c1b8793eb..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsQueueAttributes.java +++ /dev/null @@ -1,105 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.sqs; - -import com.amazonaws.services.sqs.model.QueueAttributeName; -import jakarta.annotation.PostConstruct; -import lombok.Getter; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.StringUtils; - -import java.util.HashMap; -import java.util.Map; - -@Component -@ConditionalOnExpression("'${queue.type:null}'=='aws-sqs'") -public class TbAwsSqsQueueAttributes { - @Value("${queue.aws-sqs.queue-properties.core:}") - private String coreProperties; - @Value("${queue.aws-sqs.queue-properties.rule-engine:}") - private String ruleEngineProperties; - @Value("${queue.aws-sqs.queue-properties.transport-api:}") - private String transportApiProperties; - @Value("${queue.aws-sqs.queue-properties.notifications:}") - private String notificationsProperties; - @Value("${queue.aws-sqs.queue-properties.js-executor:}") - private String jsExecutorProperties; - @Value("${queue.aws-sqs.queue-properties.ota-updates:}") - private String otaProperties; - @Value("${queue.aws-sqs.queue-properties.version-control:}") - private String vcProperties; - @Value("${queue.aws-sqs.queue-properties.edge:}") - private String edgeProperties; - - @Getter - private Map coreAttributes; - @Getter - private Map ruleEngineAttributes; - @Getter - private Map transportApiAttributes; - @Getter - private Map notificationsAttributes; - @Getter - private Map jsExecutorAttributes; - @Getter - private Map otaAttributes; - @Getter - private Map vcAttributes; - @Getter - private Map edgeAttributes; - - private final Map defaultAttributes = new HashMap<>(); - - @PostConstruct - private void init() { - defaultAttributes.put(QueueAttributeName.FifoQueue.toString(), "true"); - - coreAttributes = getConfigs(coreProperties); - ruleEngineAttributes = getConfigs(ruleEngineProperties); - transportApiAttributes = getConfigs(transportApiProperties); - notificationsAttributes = getConfigs(notificationsProperties); - jsExecutorAttributes = getConfigs(jsExecutorProperties); - otaAttributes = getConfigs(otaProperties); - vcAttributes = getConfigs(vcProperties); - edgeAttributes = getConfigs(edgeProperties); - } - - private Map getConfigs(String properties) { - Map configs = new HashMap<>(defaultAttributes); - configs.putAll(toConfigs(properties)); - return configs; - } - - public static Map toConfigs(String properties) { - Map configs = new HashMap<>(); - if (StringUtils.isNotEmpty(properties)) { - for (String property : properties.split(";")) { - int delimiterPosition = property.indexOf(":"); - String key = property.substring(0, delimiterPosition); - String value = property.substring(delimiterPosition + 1); - validateAttributeName(key); - configs.put(key, value); - } - } - return configs; - } - - private static void validateAttributeName(String key) { - QueueAttributeName.fromValue(key); - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsSettings.java b/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsSettings.java deleted file mode 100644 index 122d5f0780..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsSettings.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.sqs; - -import lombok.Data; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.stereotype.Component; - -@Slf4j -@ConditionalOnExpression("'${queue.type:null}'=='aws-sqs'") -@Component -@Data -public class TbAwsSqsSettings { - - @Value("${queue.aws_sqs.use_default_credential_provider_chain}") - private Boolean useDefaultCredentialProviderChain; - - @Value("${queue.aws_sqs.access_key_id}") - private String accessKeyId; - - @Value("${queue.aws_sqs.secret_access_key}") - private String secretAccessKey; - - @Value("${queue.aws_sqs.region}") - private String region; - - @Value("${queue.aws_sqs.threads_per_topic}") - private int threadsPerTopic; - - @Value("${queue.aws_sqs.producer_thread_pool_size:50}") - private int threadPoolSize; - -} diff --git a/common/queue/src/test/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqConsumerTemplateTest.java b/common/queue/src/test/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqConsumerTemplateTest.java deleted file mode 100644 index 2d6a50cea4..0000000000 --- a/common/queue/src/test/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqConsumerTemplateTest.java +++ /dev/null @@ -1,128 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.queue.rabbitmq; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; -import com.rabbitmq.client.ConnectionFactory; -import com.rabbitmq.client.GetResponse; -import java.nio.charset.StandardCharsets; -import java.util.Set; -import java.util.UUID; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; -import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.TbQueueMsgDecoder; -import org.thingsboard.server.queue.common.DefaultTbQueueMsg; - -@ExtendWith(MockitoExtension.class) -class TbRabbitMqConsumerTemplateTest { - - private static final String TOPIC = "some-topic"; - - @Mock - private TbQueueAdmin admin; - - @Mock - private ConnectionFactory connectionFactory; - - @Mock - private TbQueueMsgDecoder decoder; - - @Mock - private Connection connection; - - @Mock - private Channel channel; - - @Mock - private TopicPartitionInfo partition; - - @Mock - private GetResponse getResponse; - - private TbRabbitMqConsumerTemplate consumer; - - private void setUpConsumerWithMaxPollMessages(int maxPollMessages) throws Exception { - when(connectionFactory.newConnection()).thenReturn(connection); - when(connection.createChannel()).thenReturn(channel); - TbRabbitMqSettings settings = new TbRabbitMqSettings(); - settings.setMaxPollMessages(maxPollMessages); - settings.setConnectionFactory(connectionFactory); - - consumer = new TbRabbitMqConsumerTemplate<>(admin, settings, TOPIC, decoder); - when(partition.getFullTopicName()).thenReturn(TOPIC); - consumer.subscribe(Set.of(partition)); - } - - @Test - void pollWithMax5PollMessagesReturnsEmptyListIfNoMessages() throws Exception { - setUpConsumerWithMaxPollMessages(5); - when(channel.basicGet(anyString(), anyBoolean())).thenReturn(null); - - assertThat(consumer.poll(0L)).isEmpty(); - - verify(channel).basicGet(anyString(), anyBoolean()); - } - - @Test - void pollWithMax5PollMessagesReturns5MessagesIfQueueContains5() throws Exception { - setUpConsumerWithMaxPollMessages(5); - when(getResponse.getBody()).thenReturn(newMessageBody()); - when(channel.basicGet(anyString(), anyBoolean())).thenReturn(getResponse); - - assertThat(consumer.poll(0L)).hasSize(5); - - verify(channel, times(5)).basicGet(anyString(), anyBoolean()); - } - - @Test - void pollWithMax1PollMessageReturns1MessageIfQueueContainsMore() throws Exception { - setUpConsumerWithMaxPollMessages(1); - when(getResponse.getBody()).thenReturn(newMessageBody()); - when(channel.basicGet(anyString(), anyBoolean())).thenReturn(getResponse); - - assertThat(consumer.poll(0L)).hasSize(1); - - verify(channel).basicGet(anyString(), anyBoolean()); - } - - @Test - void pollWithMax3PollMessagesReturns2MessagesIfQueueContains2() throws Exception { - setUpConsumerWithMaxPollMessages(3); - when(getResponse.getBody()).thenReturn(newMessageBody()); - when(channel.basicGet(anyString(), anyBoolean())).thenReturn(getResponse, getResponse, null); - - assertThat(consumer.poll(0L)).hasSize(2); - - verify(channel, times(3)).basicGet(anyString(), anyBoolean()); - } - - private byte[] newMessageBody() { - return ("{\"key\": \"" + UUID.randomUUID() + "\"}").getBytes(StandardCharsets.UTF_8); - } - -} \ No newline at end of file diff --git a/msa/js-executor/config/custom-environment-variables.yml b/msa/js-executor/config/custom-environment-variables.yml index 7c79eda0d1..f28df78d1d 100644 --- a/msa/js-executor/config/custom-environment-variables.yml +++ b/msa/js-executor/config/custom-environment-variables.yml @@ -14,7 +14,7 @@ # limitations under the License. # -queue_type: "TB_QUEUE_TYPE" #kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) +queue_type: "TB_QUEUE_TYPE" #kafka (Apache Kafka) queue_prefix: "TB_QUEUE_PREFIX" request_topic: "REMOTE_JS_EVAL_REQUEST_TOPIC" http_port: "HTTP_PORT" # /livenessProbe @@ -55,32 +55,6 @@ kafka: username: "TB_QUEUE_KAFKA_CONFLUENT_USERNAME" password: "TB_QUEUE_KAFKA_CONFLUENT_PASSWORD" -pubsub: - project_id: "TB_QUEUE_PUBSUB_PROJECT_ID" - service_account: "TB_QUEUE_PUBSUB_SERVICE_ACCOUNT" - queue_properties: "TB_QUEUE_PUBSUB_JE_QUEUE_PROPERTIES" - -aws_sqs: - access_key_id: "TB_QUEUE_AWS_SQS_ACCESS_KEY_ID" - secret_access_key: "TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY" - region: "TB_QUEUE_AWS_SQS_REGION" - queue_properties: "TB_QUEUE_AWS_SQS_JE_QUEUE_PROPERTIES" - -rabbitmq: - host: "TB_QUEUE_RABBIT_MQ_HOST" - port: "TB_QUEUE_RABBIT_MQ_PORT" - virtual_host: "TB_QUEUE_RABBIT_MQ_VIRTUAL_HOST" - username: "TB_QUEUE_RABBIT_MQ_USERNAME" - password: "TB_QUEUE_RABBIT_MQ_PASSWORD" - queue_properties: "TB_QUEUE_RABBIT_MQ_JE_QUEUE_PROPERTIES" - -service_bus: - namespace_name: "TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME" - sas_key_name: "TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME" - sas_key: "TB_QUEUE_SERVICE_BUS_SAS_KEY" - max_messages: "TB_QUEUE_SERVICE_BUS_MAX_MESSAGES" - queue_properties: "TB_QUEUE_SERVICE_BUS_JE_QUEUE_PROPERTIES" - logger: level: "LOGGER_LEVEL" path: "LOG_FOLDER" diff --git a/msa/js-executor/config/default.yml b/msa/js-executor/config/default.yml index f5bde183e4..dda3ef02c7 100644 --- a/msa/js-executor/config/default.yml +++ b/msa/js-executor/config/default.yml @@ -44,23 +44,6 @@ kafka: sasl: mechanism: "PLAIN" -pubsub: - queue_properties: "ackDeadlineInSec:30;messageRetentionInSec:604800" - -aws_sqs: - queue_properties: "VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800" - -rabbitmq: - host: "localhost" - port: "5672" - virtual_host: "/" - username: "admin" - password: "password" - queue_properties: "x-max-length-bytes:1048576000;x-message-ttl:604800000" - -service_bus: - queue_properties: "lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800" - logger: level: "info" path: "logs" diff --git a/msa/js-executor/queue/awsSqsTemplate.ts b/msa/js-executor/queue/awsSqsTemplate.ts deleted file mode 100644 index f61f684bf4..0000000000 --- a/msa/js-executor/queue/awsSqsTemplate.ts +++ /dev/null @@ -1,198 +0,0 @@ -/// -/// 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 config from 'config'; -import { _logger } from '../config/logger'; -import { JsInvokeMessageProcessor } from '../api/jsInvokeMessageProcessor' -import { IQueue } from './queue.models'; -import { - CreateQueueCommand, - CreateQueueRequest, - DeleteMessageBatchCommand, - DeleteMessageBatchRequest, - DeleteMessageBatchRequestEntry, - ListQueuesCommand, - ListQueuesResult, - ReceiveMessageCommand, - ReceiveMessageRequest, - ReceiveMessageResult, - SendMessageCommand, - SendMessageRequest, - SQSClient -} from '@aws-sdk/client-sqs'; -import uuid from 'uuid-random'; - -export class AwsSqsTemplate implements IQueue { - - private logger = _logger(`awsSqsTemplate`); - private queuePrefix: string = config.get('queue_prefix'); - private requestTopic: string = this.queuePrefix ? this.queuePrefix + "." + config.get('request_topic') : config.get('request_topic'); - private accessKeyId: string = config.get('aws_sqs.access_key_id'); - private secretAccessKey: string = config.get('aws_sqs.secret_access_key'); - private region: string = config.get('aws_sqs.region'); - private queueProperties: string = config.get('aws_sqs.queue_properties'); - private pollInterval = Number(config.get('js.response_poll_interval')); - - private sqsClient: SQSClient; - private requestQueueURL: string - private queueUrls = new Map(); - private queueAttributes: { [n: string]: string } = { - FifoQueue: 'true' - }; - private timer: NodeJS.Timer; - - name = 'AWS SQS'; - - constructor() { - } - - async init() { - this.sqsClient = new SQSClient({ - apiVersion: '2012-11-05', - credentials: { - accessKeyId: this.accessKeyId, - secretAccessKey: this.secretAccessKey - }, - region: this.region - }); - - const queues = await this.getQueues(); - - if (queues.QueueUrls) { - queues.QueueUrls.forEach(queueUrl => { - const delimiterPosition = queueUrl.lastIndexOf('/'); - const queueName = queueUrl.substring(delimiterPosition + 1); - this.queueUrls.set(queueName, queueUrl); - }); - } - - this.parseQueueProperties(); - - this.requestQueueURL = this.queueUrls.get(AwsSqsTemplate.topicToSqsQueueName(this.requestTopic)) || ''; - if (!this.requestQueueURL) { - this.requestQueueURL = await this.createQueue(this.requestTopic); - } - - const messageProcessor = new JsInvokeMessageProcessor(this); - - const params: ReceiveMessageRequest = { - MaxNumberOfMessages: 10, - QueueUrl: this.requestQueueURL, - WaitTimeSeconds: Math.ceil(this.pollInterval / 10) - }; - this.timer = setTimeout(() => {this.getAndProcessMessage(messageProcessor, params)}, this.pollInterval); - } - - private async getAndProcessMessage(messageProcessor: JsInvokeMessageProcessor, params: ReceiveMessageRequest) { - const messagesResponse: ReceiveMessageResult = await this.sqsClient.send(new ReceiveMessageCommand(params)); - const messages = messagesResponse.Messages; - - if (messages && messages.length > 0) { - const entries: DeleteMessageBatchRequestEntry[] = []; - - messages.forEach(message => { - entries.push({ - Id: message.MessageId, - ReceiptHandle: message.ReceiptHandle - }); - messageProcessor.onJsInvokeMessage(JSON.parse(message.Body || '')); - }); - - const deleteBatch: DeleteMessageBatchRequest = { - QueueUrl: this.requestQueueURL, - Entries: entries - }; - try { - await this.sqsClient.send(new DeleteMessageBatchCommand(deleteBatch)) - } catch (err: any) { - this.logger.error("Failed to delete messages from queue.", err.message); - } - } - this.timer = setTimeout(() => {this.getAndProcessMessage(messageProcessor, params)}, this.pollInterval); - } - - async send(responseTopic: string, msgKey: string, rawResponse: Buffer, headers: any): Promise { - let msgBody = JSON.stringify( - { - key: msgKey, - data: [...rawResponse], - headers: headers - }); - - let responseQueueUrl = this.queueUrls.get(AwsSqsTemplate.topicToSqsQueueName(responseTopic)); - - if (!responseQueueUrl) { - responseQueueUrl = await this.createQueue(responseTopic); - this.queueUrls.set(responseTopic, responseQueueUrl); - } - - let msgId = uuid(); - - let params: SendMessageRequest = { - MessageBody: msgBody, - QueueUrl: responseQueueUrl, - MessageGroupId: msgId, - MessageDeduplicationId: msgId - }; - - return this.sqsClient.send(new SendMessageCommand(params)) - } - - private async getQueues(): Promise { - return this.sqsClient.send(new ListQueuesCommand({})); - } - - private parseQueueProperties() { - const props = this.queueProperties.split(';'); - props.forEach(p => { - const delimiterPosition = p.indexOf(':'); - this.queueAttributes[p.substring(0, delimiterPosition)] = p.substring(delimiterPosition + 1); - }); - } - - private static topicToSqsQueueName(topic: string): string { - return topic.replace(/\./g, '_') + '.fifo'; - } - - private async createQueue(topic: string): Promise { - let queueName = AwsSqsTemplate.topicToSqsQueueName(topic); - let queueParams: CreateQueueRequest = { - QueueName: queueName, - Attributes: this.queueAttributes - }; - - const result = await this.sqsClient.send(new CreateQueueCommand(queueParams)); - return result.QueueUrl || ''; - } - - async destroy(): Promise { - this.logger.info('Stopping AWS SQS resources...'); - clearTimeout(this.timer); - if (this.sqsClient) { - this.logger.info('Stopping AWS SQS client...'); - try { - const _sqsClient = this.sqsClient; - // @ts-ignore - delete this.sqsClient; - _sqsClient.destroy(); - this.logger.info('AWS SQS client stopped.'); - } catch (e: any) { - this.logger.info('AWS SQS client stop error.'); - } - } - this.logger.info('AWS SQS resources stopped.') - } -} diff --git a/msa/js-executor/queue/kafkaTemplate.ts b/msa/js-executor/queue/kafkaTemplate.ts index 659d1fb8b2..309edca61d 100644 --- a/msa/js-executor/queue/kafkaTemplate.ts +++ b/msa/js-executor/queue/kafkaTemplate.ts @@ -35,6 +35,7 @@ import { KeyObject } from 'tls'; import process, { exit, kill } from 'process'; +// TODO: remove dependencies for other queue types export class KafkaTemplate implements IQueue { private logger = _logger(`kafkaTemplate`); diff --git a/msa/js-executor/queue/pubSubTemplate.ts b/msa/js-executor/queue/pubSubTemplate.ts deleted file mode 100644 index 537d2e71b1..0000000000 --- a/msa/js-executor/queue/pubSubTemplate.ts +++ /dev/null @@ -1,162 +0,0 @@ -/// -/// 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 config from 'config'; -import { _logger } from '../config/logger'; -import { JsInvokeMessageProcessor } from '../api/jsInvokeMessageProcessor' -import { PubSub } from '@google-cloud/pubsub'; -import { IQueue } from './queue.models'; -import { Message } from '@google-cloud/pubsub/build/src/subscriber'; - -export class PubSubTemplate implements IQueue { - - private logger = _logger(`pubSubTemplate`); - private projectId: string = config.get('pubsub.project_id'); - private credentials = JSON.parse(config.get('pubsub.service_account')); - private queuePrefix: string = config.get('queue_prefix'); - private requestTopic: string = this.queuePrefix ? this.queuePrefix + "." + config.get('request_topic') : config.get('request_topic'); - private queueProperties: string = config.get('pubsub.queue_properties'); - - private pubSubClient: PubSub; - private queueProps: { [n: string]: string } = {}; - private topics: string[] = []; - private subscriptions: string[] = []; - - name = 'Pub/Sub'; - - constructor() { - } - - async init() { - this.pubSubClient = new PubSub({ - projectId: this.projectId, - credentials: this.credentials - }); - - this.parseQueueProperties(); - - const topicList = await this.pubSubClient.getTopics(); - - if (topicList) { - topicList[0].forEach(topic => { - this.topics.push(PubSubTemplate.getName(topic.name)); - }); - } - - const subscriptionList = await this.pubSubClient.getSubscriptions(); - - if (subscriptionList) { - topicList[0].forEach(sub => { - this.subscriptions.push(PubSubTemplate.getName(sub.name)); - }); - } - - if (!(this.subscriptions.includes(this.requestTopic) && this.topics.includes(this.requestTopic))) { - await this.createTopic(this.requestTopic); - await this.createSubscription(this.requestTopic); - } - - const subscription = this.pubSubClient.subscription(this.requestTopic); - - const messageProcessor = new JsInvokeMessageProcessor(this); - - const messageHandler = (message: Message) => { - messageProcessor.onJsInvokeMessage(JSON.parse(message.data.toString('utf8'))); - message.ack(); - }; - - subscription.on('message', messageHandler); - } - - async send(responseTopic: string, msgKey: string, rawResponse: Buffer, headers: any): Promise { - if (!(this.subscriptions.includes(responseTopic) && this.topics.includes(this.requestTopic))) { - await this.createTopic(this.requestTopic); - await this.createSubscription(this.requestTopic); - } - - let data = JSON.stringify( - { - key: msgKey, - data: [...rawResponse], - headers: headers - }); - let dataBuffer = Buffer.from(data); - return this.pubSubClient.topic(responseTopic).publishMessage({data: dataBuffer}); - } - - private parseQueueProperties() { - const props = this.queueProperties.split(';'); - props.forEach(p => { - const delimiterPosition = p.indexOf(':'); - this.queueProps[p.substring(0, delimiterPosition)] = p.substring(delimiterPosition + 1); - }); - } - - private static getName(fullName: string): string { - const delimiterPosition = fullName.lastIndexOf('/'); - return fullName.substring(delimiterPosition + 1); - } - - private async createTopic(topic: string) { - if (!this.topics.includes(topic)) { - try { - await this.pubSubClient.createTopic(topic); - this.logger.info('Created new Pub/Sub topic: %s', topic); - } catch (e) { - this.logger.info('Pub/Sub topic already exists'); - } - this.topics.push(topic); - } - } - - private async createSubscription(topic: string) { - if (!this.subscriptions.includes(topic)) { - try { - await this.pubSubClient.createSubscription(topic, topic, { - topic: topic, - name: topic, - ackDeadlineSeconds: Number(this.queueProps['ackDeadlineInSec']), - messageRetentionDuration: { - seconds: this.queueProps['messageRetentionInSec'] - } - }); - this.logger.info('Created new Pub/Sub subscription: %s', topic); - } catch (e) { - this.logger.info('Pub/Sub subscription already exists.'); - } - - this.subscriptions.push(topic); - } - } - - async destroy(): Promise { - this.logger.info('Stopping Pub/Sub resources...'); - if (this.pubSubClient) { - this.logger.info('Stopping Pub/Sub client...'); - try { - const _pubSubClient = this.pubSubClient; - // @ts-ignore - delete this.pubSubClient; - await _pubSubClient.close(); - this.logger.info('Pub/Sub client stopped.'); - } catch (e) { - this.logger.info('Pub/Sub client stop error.'); - } - } - this.logger.info('Pub/Sub resources stopped.'); - } -} - diff --git a/msa/js-executor/queue/rabbitmqTemplate.ts b/msa/js-executor/queue/rabbitmqTemplate.ts deleted file mode 100644 index 9219afedc9..0000000000 --- a/msa/js-executor/queue/rabbitmqTemplate.ts +++ /dev/null @@ -1,128 +0,0 @@ -/// -/// 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 config from 'config'; -import { _logger } from '../config/logger'; -import { JsInvokeMessageProcessor } from '../api/jsInvokeMessageProcessor' -import { IQueue } from './queue.models'; -import amqp, { ConfirmChannel, Connection } from 'amqplib'; -import { Options, Replies } from 'amqplib/properties'; - -export class RabbitMqTemplate implements IQueue { - - private logger = _logger(`rabbitmqTemplate`); - private queuePrefix: string = config.get('queue_prefix'); - private requestTopic: string = this.queuePrefix ? this.queuePrefix + "." + config.get('request_topic') : config.get('request_topic'); - private host = config.get('rabbitmq.host'); - private port = config.get('rabbitmq.port'); - private vhost = config.get('rabbitmq.virtual_host'); - private username = config.get('rabbitmq.username'); - private password = config.get('rabbitmq.password'); - private queueProperties: string = config.get('rabbitmq.queue_properties'); - - private queueOptions: Options.AssertQueue = { - durable: false, - exclusive: false, - autoDelete: false - }; - private connection: Connection; - private channel: ConfirmChannel; - private topics: string[] = []; - - name = 'RabbitMQ'; - - constructor() { - } - - async init(): Promise { - const url = `amqp://${this.username}:${this.password}@${this.host}:${this.port}${this.vhost}`; - this.connection = await amqp.connect(url); - this.channel = await this.connection.createConfirmChannel(); - - this.parseQueueProperties(); - - await this.createQueue(this.requestTopic); - - const messageProcessor = new JsInvokeMessageProcessor(this); - - await this.channel.consume(this.requestTopic, (message) => { - if (message) { - messageProcessor.onJsInvokeMessage(JSON.parse(message.content.toString('utf8'))); - this.channel.ack(message); - } - }) - } - - async send(responseTopic: string, msgKey: string, rawResponse: Buffer, headers: any): Promise { - - if (!this.topics.includes(responseTopic)) { - await this.createQueue(responseTopic); - this.topics.push(responseTopic); - } - - let data = JSON.stringify( - { - key: msgKey, - data: [...rawResponse], - headers: headers - }); - let dataBuffer = Buffer.from(data); - this.channel.sendToQueue(responseTopic, dataBuffer); - return this.channel.waitForConfirms() - } - - private parseQueueProperties() { - let args: { [n: string]: number } = {}; - const props = this.queueProperties.split(';'); - props.forEach(p => { - const delimiterPosition = p.indexOf(':'); - args[p.substring(0, delimiterPosition)] = Number(p.substring(delimiterPosition + 1)); - }); - this.queueOptions['arguments'] = args; - } - - private async createQueue(topic: string): Promise { - return this.channel.assertQueue(topic, this.queueOptions); - } - - async destroy() { - this.logger.info('Stopping RabbitMQ resources...'); - - if (this.channel) { - this.logger.info('Stopping RabbitMQ chanel...'); - const _channel = this.channel; - // @ts-ignore - delete this.channel; - await _channel.close(); - this.logger.info('RabbitMQ chanel stopped'); - } - - if (this.connection) { - this.logger.info('Stopping RabbitMQ connection...') - try { - const _connection = this.connection; - // @ts-ignore - delete this.connection; - await _connection.close(); - this.logger.info('RabbitMQ client connection.'); - } catch (e) { - this.logger.info('RabbitMQ connection stop error.'); - } - } - this.logger.info('RabbitMQ resources stopped.') - } - -} diff --git a/msa/js-executor/queue/serviceBusTemplate.ts b/msa/js-executor/queue/serviceBusTemplate.ts deleted file mode 100644 index da5a72673f..0000000000 --- a/msa/js-executor/queue/serviceBusTemplate.ts +++ /dev/null @@ -1,175 +0,0 @@ -/// -/// 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 config from 'config'; -import { _logger } from '../config/logger'; -import { JsInvokeMessageProcessor } from '../api/jsInvokeMessageProcessor' -import { IQueue } from './queue.models'; -import { - CreateQueueOptions, - ProcessErrorArgs, - ServiceBusAdministrationClient, - ServiceBusClient, - ServiceBusReceivedMessage, - ServiceBusReceiver, - ServiceBusSender -} from '@azure/service-bus'; - -export class ServiceBusTemplate implements IQueue { - - private logger = _logger(`serviceBusTemplate`); - private queuePrefix: string = config.get('queue_prefix'); - private requestTopic: string = this.queuePrefix ? this.queuePrefix + "." + config.get('request_topic') : config.get('request_topic'); - private namespaceName = config.get('service_bus.namespace_name'); - private sasKeyName = config.get('service_bus.sas_key_name'); - private sasKey = config.get('service_bus.sas_key'); - private queueProperties: string = config.get('service_bus.queue_properties'); - - private sbClient: ServiceBusClient; - private serviceBusService: ServiceBusAdministrationClient; - private queueOptions: CreateQueueOptions = {}; - private queues: string[] = []; - private receiver: ServiceBusReceiver; - private senderMap = new Map(); - - name = 'Azure Service Bus'; - - constructor() { - } - - async init() { - const connectionString = `Endpoint=sb://${this.namespaceName}.servicebus.windows.net/;SharedAccessKeyName=${this.sasKeyName};SharedAccessKey=${this.sasKey}`; - this.sbClient = new ServiceBusClient(connectionString) - this.serviceBusService = new ServiceBusAdministrationClient(connectionString); - - this.parseQueueProperties(); - - const listQueues = await this.serviceBusService.listQueues(); - for await (const queue of listQueues) { - this.queues.push(queue.name); - } - - if (!this.queues.includes(this.requestTopic)) { - await this.createQueueIfNotExist(this.requestTopic); - this.queues.push(this.requestTopic); - } - - this.receiver = this.sbClient.createReceiver(this.requestTopic, {receiveMode: 'peekLock'}); - - const messageProcessor = new JsInvokeMessageProcessor(this); - - const messageHandler = async (message: ServiceBusReceivedMessage) => { - if (message) { - messageProcessor.onJsInvokeMessage(message.body); - await this.receiver.completeMessage(message); - } - }; - const errorHandler = async (error: ProcessErrorArgs) => { - this.logger.error('Failed to receive message from queue.', error); - }; - this.receiver.subscribe({processMessage: messageHandler, processError: errorHandler}) - } - - async send(responseTopic: string, msgKey: string, rawResponse: Buffer, headers: any): Promise { - if (!this.queues.includes(this.requestTopic)) { - await this.createQueueIfNotExist(this.requestTopic); - this.queues.push(this.requestTopic); - } - - let customSender = this.senderMap.get(responseTopic); - - if (!customSender) { - customSender = this.sbClient.createSender(responseTopic); - this.senderMap.set(responseTopic, customSender); - } - - let data = { - key: msgKey, - data: [...rawResponse], - headers: headers - }; - - return customSender.sendMessages({body: data}); - } - - private parseQueueProperties() { - let properties: { [n: string]: string } = {}; - const props = this.queueProperties.split(';'); - props.forEach(p => { - const delimiterPosition = p.indexOf(':'); - properties[p.substring(0, delimiterPosition)] = p.substring(delimiterPosition + 1); - }); - this.queueOptions = { - requiresDuplicateDetection: false, - maxSizeInMegabytes: Number(properties['maxSizeInMb']), - defaultMessageTimeToLive: `PT${properties['messageTimeToLiveInSec']}S`, - lockDuration: `PT${properties['lockDurationInSec']}S` - }; - } - - private async createQueueIfNotExist(topic: string) { - try { - await this.serviceBusService.createQueue(topic, this.queueOptions) - } catch (err: any) { - if (err && err.code !== "MessageEntityAlreadyExistsError") { - throw new Error(err); - } - } - } - - async destroy() { - this.logger.info('Stopping Azure Service Bus resources...') - if (this.receiver) { - this.logger.info('Stopping Service Bus Receiver...'); - try { - const _receiver = this.receiver; - // @ts-ignore - delete this.receiver; - await _receiver.close(); - this.logger.info('Service Bus Receiver stopped.'); - } catch (e) { - this.logger.info('Service Bus Receiver stop error.'); - } - } - - this.logger.info('Stopping Service Bus Senders...'); - const senders: Promise[] = []; - this.senderMap.forEach((sender) => { - senders.push(sender.close()); - }); - this.senderMap.clear(); - try { - await Promise.all(senders); - this.logger.info('Service Bus Senders stopped.'); - } catch (e) { - this.logger.info('Service Bus Senders stop error.'); - } - - if (this.sbClient) { - this.logger.info('Stopping Service Bus Client...'); - try { - const _sbClient = this.sbClient; - // @ts-ignore - delete this.sbClient; - await _sbClient.close(); - this.logger.info('Service Bus Client stopped.'); - } catch (e) { - this.logger.info('Service Bus Client stop error.'); - } - } - this.logger.info('Azure Service Bus resources stopped.') - } -} diff --git a/msa/js-executor/server.ts b/msa/js-executor/server.ts index 5bd1f59692..50d326d1e8 100644 --- a/msa/js-executor/server.ts +++ b/msa/js-executor/server.ts @@ -19,10 +19,6 @@ import { _logger } from './config/logger'; import { HttpServer } from './api/httpServer'; import { IQueue } from './queue/queue.models'; import { KafkaTemplate } from './queue/kafkaTemplate'; -import { PubSubTemplate } from './queue/pubSubTemplate'; -import { AwsSqsTemplate } from './queue/awsSqsTemplate'; -import { RabbitMqTemplate } from './queue/rabbitmqTemplate'; -import { ServiceBusTemplate } from './queue/serviceBusTemplate'; const logger = _logger('main'); @@ -55,14 +51,6 @@ async function createQueue(serviceType: string): Promise { switch (serviceType) { case 'kafka': return new KafkaTemplate(); - case 'pubsub': - return new PubSubTemplate(); - case 'aws-sqs': - return new AwsSqsTemplate(); - case 'rabbitmq': - return new RabbitMqTemplate(); - case 'service-bus': - return new ServiceBusTemplate(); default: throw new Error('Unknown service type: ' + serviceType); } diff --git a/msa/vc-executor/src/main/resources/tb-vc-executor.yml b/msa/vc-executor/src/main/resources/tb-vc-executor.yml index a55a0e73dc..ec0aee3091 100644 --- a/msa/vc-executor/src/main/resources/tb-vc-executor.yml +++ b/msa/vc-executor/src/main/resources/tb-vc-executor.yml @@ -47,7 +47,7 @@ zk: # Queue configuration parameters queue: - type: "${TB_QUEUE_TYPE:kafka}" # in-memory or kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) + type: "${TB_QUEUE_TYPE:kafka}" # in-memory or kafka (Apache Kafka) prefix: "${TB_QUEUE_PREFIX:}" # Global queue prefix. If specified, prefix is added before default topic name: 'prefix.default_topic_name'. Prefix is applied to all topics (and consumer groups for kafka). in_memory: stats: @@ -146,91 +146,10 @@ queue: print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}" # Time to wait for the stats-loading requests to Kafka to finis kafka-response-timeout-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_RESPONSE_TIMEOUT_MS:1000}" - aws_sqs: - # Use the default credentials provider for AWS SQS - use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}" - # Access key ID from AWS IAM user - access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" - # Secret access key from AWS IAM user - secret_access_key: "${TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY:YOUR_SECRET}" - # Region from AWS account - region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}" - # Number of threads per each AWS SQS queue in consumer - threads_per_topic: "${TB_QUEUE_AWS_SQS_THREADS_PER_TOPIC:1}" - # Thread pool size for aws_sqs queue producer executor provider. Default value equals to AmazonSQSAsyncClient.DEFAULT_THREAD_POOL_SIZE - producer_thread_pool_size: "${TB_QUEUE_AWS_SQS_EXECUTOR_THREAD_POOL_SIZE:50}" - queue-properties: - # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - version-control: "${TB_QUEUE_AWS_SQS_VC_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - pubsub: - # Project ID from Google Cloud - project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}" - # API Credentials in JSON format - service_account: "${TB_QUEUE_PUBSUB_SERVICE_ACCOUNT:YOUR_SERVICE_ACCOUNT}" - # Message size for PubSub queue.Value in bytes - max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" #in bytes - # Number of messages per consumer - max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" - # Thread pool size for pubsub queue executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:0}" - queue-properties: - # Pub/Sub properties for Core subscribers, messages which will commit after ackDeadlineInSec period can be consumed again - core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - # Pub/Sub properties for Version Control subscribers, messages which will commit after ackDeadlineInSec period can be consumed again - notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - # Pub/Sub properties for Transport Api subscribers, messages which will commit after ackDeadlineInSec period can be consumed again - version-control: "${TB_QUEUE_PUBSUB_VC_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - service_bus: - # Azure namespace - namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}" - # Azure Service Bus Shared Access Signatures key name - sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}" - # Azure Service Bus Shared Access Signatures key - sas_key: "${TB_QUEUE_SERVICE_BUS_SAS_KEY:YOUR_SAS_KEY}" - # Number of messages per a consumer - max_messages: "${TB_QUEUE_SERVICE_BUS_MAX_MESSAGES:1000}" - queue-properties: - # Azure Service Bus properties for Core queues - core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - # Azure Service Bus properties for Notification queues - notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - # Azure Service Bus properties for Version Control queues - version-control: "${TB_QUEUE_SERVICE_BUS_VC_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - rabbitmq: - # By default empty - exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}" - # RabbitMQ host used to establish connection - host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}" - # RabbitMQ host used to establish a connection - port: "${TB_QUEUE_RABBIT_MQ_PORT:5672}" - # Virtual hosts provide logical grouping and separation of resources - virtual_host: "${TB_QUEUE_RABBIT_MQ_VIRTUAL_HOST:/}" - # Username for RabbitMQ user account - username: "${TB_QUEUE_RABBIT_MQ_USERNAME:YOUR_USERNAME}" - # User password for RabbitMQ user account - password: "${TB_QUEUE_RABBIT_MQ_PASSWORD:YOUR_PASSWORD}" - # Network connection between clients and RabbitMQ nodes can fail. RabbitMQ Java client supports automatic recovery of connections and topology (queues, exchanges, bindings, and consumers) - automatic_recovery_enabled: "${TB_QUEUE_RABBIT_MQ_AUTOMATIC_RECOVERY_ENABLED:false}" - # The connection timeout for the RabbitMQ connection factory - connection_timeout: "${TB_QUEUE_RABBIT_MQ_CONNECTION_TIMEOUT:60000}" - # RabbitMQ has a timeout for connection handshake. When clients run in heavily constrained environments, it may be necessary to increase the timeout - handshake_timeout: "${TB_QUEUE_RABBIT_MQ_HANDSHAKE_TIMEOUT:10000}" - queue-properties: - # RabbitMQ properties for Core queues - core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" - # RabbitMQ properties for Transport API queues - transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" - # RabbitMQ properties for Version Control queues - version-control: "${TB_QUEUE_RABBIT_MQ_VC_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" partitions: hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 core: - # Default topic name of Kafka, RabbitMQ, etc. queue + # Default topic name topic: "${TB_QUEUE_CORE_TOPIC:tb_core}" # Interval in milliseconds to poll messages by Core microservices poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}" @@ -245,7 +164,7 @@ queue: pack-interval-ms: "${TB_QUEUE_CORE_OTA_PACK_INTERVAL_MS:60000}" # The size of OTA updates notifications fetched from the queue. The queue stores pairs of firmware and device ids pack-size: "${TB_QUEUE_CORE_OTA_PACK_SIZE:100}" - # Stats topic name for queue Kafka, RabbitMQ, etc. + # Stats topic name usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}" stats: # Enable/disable statistics for Core microservices @@ -256,7 +175,7 @@ queue: # Topic name for Housekeeper tasks topic: "${TB_HOUSEKEEPER_TOPIC:tb_housekeeper}" vc: - # Default topic name for Kafka, RabbitMQ, etc. + # Default topic name topic: "${TB_QUEUE_VC_TOPIC:tb_version_control}" # Number of partitions to associate with this queue. Used for scaling the number of messages that can be processed in parallel partitions: "${TB_QUEUE_VC_PARTITIONS:10}" @@ -264,7 +183,7 @@ queue: poll-interval: "${TB_QUEUE_VC_INTERVAL_MS:25}" # Timeout before retrying all failed and timed-out messages from the processing pack pack-processing-timeout: "${TB_QUEUE_VC_PACK_PROCESSING_TIMEOUT_MS:180000}" - # Queue settings for Kafka, RabbitMQ, etc. Limit for single message size + # Limit for single queue message size msg-chunk-size: "${TB_QUEUE_VC_MSG_CHUNK_SIZE:250000}" # Version control parameters diff --git a/pom.xml b/pom.xml index 5e4cc50c7c..926102dd31 100755 --- a/pom.xml +++ b/pom.xml @@ -117,7 +117,6 @@ 1.12.701 1.128.1 2.37.1 - 3.6.7 1.6.4 1.6.1 1.9.4 @@ -2014,21 +2013,6 @@ proto-google-common-protos ${google.common.protos.version} - - com.microsoft.azure - azure-servicebus - ${azure-servicebus.version} - - - com.nimbusds - content-type - - - org.ow2.asm - asm - - - org.passay passay diff --git a/transport/coap/src/main/resources/tb-coap-transport.yml b/transport/coap/src/main/resources/tb-coap-transport.yml index f2ea0c1685..e4f808979a 100644 --- a/transport/coap/src/main/resources/tb-coap-transport.yml +++ b/transport/coap/src/main/resources/tb-coap-transport.yml @@ -232,7 +232,7 @@ coap: # Queue configuration parameters queue: - type: "${TB_QUEUE_TYPE:kafka}" # kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) + type: "${TB_QUEUE_TYPE:kafka}" # kafka (Apache Kafka)f prefix: "${TB_QUEUE_PREFIX:}" # Global queue prefix. If specified, prefix is added before default topic name: 'prefix.default_topic_name'. Prefix is applied to all topics (and consumer groups for kafka). kafka: # Kafka Bootstrap Servers @@ -307,94 +307,6 @@ queue: notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:52428800;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" # Kafka properties for Housekeeper tasks topic housekeeper: "${TB_QUEUE_KAFKA_HOUSEKEEPER_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:52428800;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}" - aws_sqs: - # Use the default credentials provider for AWS SQS - use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}" - # Access key ID from AWS IAM user - access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" - # Secret access key from AWS IAM user - secret_access_key: "${TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY:YOUR_SECRET}" - # Region from AWS account - region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}" - # Number of threads per each AWS SQS queue in consumer - threads_per_topic: "${TB_QUEUE_AWS_SQS_THREADS_PER_TOPIC:1}" - # Thread pool size for aws_sqs queue producer executor provider. Default value equals to AmazonSQSAsyncClient.DEFAULT_THREAD_POOL_SIZE - producer_thread_pool_size: "${TB_QUEUE_AWS_SQS_EXECUTOR_THREAD_POOL_SIZE:50}" - queue-properties: - # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - rule-engine: "${TB_QUEUE_AWS_SQS_RE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - pubsub: - # Project ID from Google Cloud - project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}" - # API Credentials in JSON format - service_account: "${TB_QUEUE_PUBSUB_SERVICE_ACCOUNT:YOUR_SERVICE_ACCOUNT}" - # Message size for PubSub queue. Value in bytes - max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" - # Number of messages per consumer - max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" - # Thread pool size for pubsub queue executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:0}" - queue-properties: - # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again - rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - # Pub/Sub properties for Core subscribers, messages which will commit after ackDeadlineInSec period can be consumed again - core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - # Pub/Sub properties for Transport API subscribers, messages which will commit after ackDeadlineInSec period can be consumed again - transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - # Pub/Sub properties for Version Control subscribers, messages which will commit after ackDeadlineInSec period can be consumed again - notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - service_bus: - # Azure namespace - namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}" - # Azure Service Bus Shared Access Signatures key name - sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}" - # Azure Service Bus Shared Access Signatures key - sas_key: "${TB_QUEUE_SERVICE_BUS_SAS_KEY:YOUR_SAS_KEY}" - # Number of messages per a consumer - max_messages: "${TB_QUEUE_SERVICE_BUS_MAX_MESSAGES:1000}" - queue-properties: - # Azure Service Bus properties for Rule Engine queues - rule-engine: "${TB_QUEUE_SERVICE_BUS_RE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - # Azure Service Bus properties for Core queues - core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - # Azure Service Bus properties for Transport Api queues - transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - # Azure Service Bus properties for Notification queues - notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - rabbitmq: - # By default empty - exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}" - # RabbitMQ host used to establish connection - host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}" - # RabbitMQ host used to establish a connection - port: "${TB_QUEUE_RABBIT_MQ_PORT:5672}" - # Virtual hosts provide logical grouping and separation of resources - virtual_host: "${TB_QUEUE_RABBIT_MQ_VIRTUAL_HOST:/}" - # Username for RabbitMQ user account - username: "${TB_QUEUE_RABBIT_MQ_USERNAME:YOUR_USERNAME}" - # User password for RabbitMQ user account - password: "${TB_QUEUE_RABBIT_MQ_PASSWORD:YOUR_PASSWORD}" - # Network connection between clients and RabbitMQ nodes can fail. RabbitMQ Java client supports automatic recovery of connections and topology (queues, exchanges, bindings, and consumers) - automatic_recovery_enabled: "${TB_QUEUE_RABBIT_MQ_AUTOMATIC_RECOVERY_ENABLED:false}" - # The connection timeout for the RabbitMQ connection factory - connection_timeout: "${TB_QUEUE_RABBIT_MQ_CONNECTION_TIMEOUT:60000}" - # RabbitMQ has a timeout for connection handshake. When clients run in heavily constrained environments, it may be necessary to increase the timeout - handshake_timeout: "${TB_QUEUE_RABBIT_MQ_HANDSHAKE_TIMEOUT:10000}" - queue-properties: - # RabbitMQ properties for Rule Engine queues - rule-engine: "${TB_QUEUE_RABBIT_MQ_RE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" - # RabbitMQ properties for Core queues - core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" - # RabbitMQ properties for Transport API queues - transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" - # RabbitMQ properties for Notification queues - notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" partitions: hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 transport_api: @@ -413,7 +325,7 @@ queue: # Interval in milliseconds to poll api response from transport microservices response_poll_interval: "${TB_QUEUE_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}" core: - # Default topic name of Kafka, RabbitMQ, etc. queue + # Default topic name topic: "${TB_QUEUE_CORE_TOPIC:tb_core}" # Interval in milliseconds to poll messages by Core microservices poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}" @@ -421,7 +333,7 @@ queue: partitions: "${TB_QUEUE_CORE_PARTITIONS:10}" # Timeout for processing a message pack by Core microservices pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}" - # Default topic name for queue Kafka, RabbitMQ, etc. + # Default topic name usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}" stats: # Enable/disable statistics for Core microservices diff --git a/transport/http/src/main/resources/tb-http-transport.yml b/transport/http/src/main/resources/tb-http-transport.yml index 527715b8ca..be91e4fe67 100644 --- a/transport/http/src/main/resources/tb-http-transport.yml +++ b/transport/http/src/main/resources/tb-http-transport.yml @@ -202,7 +202,7 @@ transport: # Queue configuration parameters queue: - type: "${TB_QUEUE_TYPE:kafka}" # kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) + type: "${TB_QUEUE_TYPE:kafka}" # kafka (Apache Kafka) prefix: "${TB_QUEUE_PREFIX:}" # Global queue prefix. If specified, prefix is added before default topic name: 'prefix.default_topic_name'. Prefix is applied to all topics (and consumer groups for kafka) . kafka: # Kafka Bootstrap Servers @@ -276,95 +276,6 @@ queue: notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:52428800;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" # Kafka properties for Housekeeper tasks topic housekeeper: "${TB_QUEUE_KAFKA_HOUSEKEEPER_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:52428800;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}" - aws_sqs: - # Use default credentials provider for AWS SQS - use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}" - # Access key ID from AWS IAM user - access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" - # Secret access key from AWS IAM user - secret_access_key: "${TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY:YOUR_SECRET}" - # Region from AWS account - region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}" - # Number of threads per each AWS SQS queue in consumer - threads_per_topic: "${TB_QUEUE_AWS_SQS_THREADS_PER_TOPIC:1}" - # Thread pool size for aws_sqs queue producer executor provider. Default value equals to AmazonSQSAsyncClient.DEFAULT_THREAD_POOL_SIZE - producer_thread_pool_size: "${TB_QUEUE_AWS_SQS_EXECUTOR_THREAD_POOL_SIZE:50}" - queue-properties: - # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - rule-engine: "${TB_QUEUE_AWS_SQS_RE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - pubsub: - # Project ID from Google Cloud - project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}" - # API Credentials in JSON format - service_account: "${TB_QUEUE_PUBSUB_SERVICE_ACCOUNT:YOUR_SERVICE_ACCOUNT}" - # Message size for PubSub queue.Value in bytes - max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" - # Number of messages per a consumer - max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" - # Thread pool size for pubsub queue executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:0}" - queue-properties: - # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consume again - rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - # Pub/Sub properties for Core subscribers, messages which will commit after ackDeadlineInSec period can be consume again - core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - # Pub/Sub properties for Transport API subscribers, messages which will commit after ackDeadlineInSec period can be consume again - transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - # Pub/Sub properties for Version Control subscribers, messages which will commit after ackDeadlineInSec period can be consume again - notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - service_bus: - # Azure namespace - namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}" - # Azure Service Bus Shared Access Signatures key name - sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}" - # Azure Service Bus Shared Access Signatures key - sas_key: "${TB_QUEUE_SERVICE_BUS_SAS_KEY:YOUR_SAS_KEY}" - # Number of messages per a consumer - max_messages: "${TB_QUEUE_SERVICE_BUS_MAX_MESSAGES:1000}" - queue-properties: - # Azure Service Bus properties for Rule Engine queues - rule-engine: "${TB_QUEUE_SERVICE_BUS_RE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - # Azure Service Bus properties for Core queues - core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - # Azure Service Bus properties for Transport Api queues - transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - # Azure Service Bus properties for Notification queues - notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - rabbitmq: - # By default empty - exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}" - # RabbitMQ host used to establish connection - host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}" - # RabbitMQ host used to establish a connection - port: "${TB_QUEUE_RABBIT_MQ_PORT:5672}" - # Virtual hosts provide logical grouping and separation of resources - virtual_host: "${TB_QUEUE_RABBIT_MQ_VIRTUAL_HOST:/}" - # Username for RabbitMQ user account - username: "${TB_QUEUE_RABBIT_MQ_USERNAME:YOUR_USERNAME}" - # User password for RabbitMQ user account - password: "${TB_QUEUE_RABBIT_MQ_PASSWORD:YOUR_PASSWORD}" - # Network connection between clients and RabbitMQ nodes can fail. RabbitMQ Java client supports automatic recovery of connections and topology (queues, exchanges, bindings, and consumers) - automatic_recovery_enabled: "${TB_QUEUE_RABBIT_MQ_AUTOMATIC_RECOVERY_ENABLED:false}" - # The connection timeout for the RabbitMQ connection factory - connection_timeout: "${TB_QUEUE_RABBIT_MQ_CONNECTION_TIMEOUT:60000}" - # RabbitMQ has a timeout for connection handshake. When clients run in heavily constrained environments, it may be necessary to increase the timeout - handshake_timeout: "${TB_QUEUE_RABBIT_MQ_HANDSHAKE_TIMEOUT:10000}" - queue-properties: - # RabbitMQ properties for Rule Engine queues - rule-engine: "${TB_QUEUE_RABBIT_MQ_RE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" - # RabbitMQ properties for Core queues - core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" - # RabbitMQ properties for Transport API queues - transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" - # RabbitMQ properties for Notification queues - notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" partitions: hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 transport_api: @@ -383,7 +294,7 @@ queue: # Interval in milliseconds to poll api response from transport microservices response_poll_interval: "${TB_QUEUE_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}" core: - # Default topic name of Kafka, RabbitMQ, etc. queue + # Default topic name topic: "${TB_QUEUE_CORE_TOPIC:tb_core}" # Interval in milliseconds to poll messages by Core microservices poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}" @@ -391,7 +302,7 @@ queue: partitions: "${TB_QUEUE_CORE_PARTITIONS:10}" # Timeout for processing a message pack by Core microservices pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}" - # Default topic name for queue Kafka, RabbitMQ, etc. + # Default topic name usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}" stats: # Enable/disable statistics for Core microservices diff --git a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml index bf6a159106..7bdd68baf1 100644 --- a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml +++ b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml @@ -302,7 +302,7 @@ transport: # Queue configuration properties queue: - type: "${TB_QUEUE_TYPE:kafka}" # kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) + type: "${TB_QUEUE_TYPE:kafka}" # kafka (Apache Kafka) prefix: "${TB_QUEUE_PREFIX:}" # Global queue prefix. If specified, prefix is added before default topic name: 'prefix.default_topic_name'. Prefix is applied to all topics (and consumer groups for kafka). kafka: # Kafka Bootstrap Servers @@ -377,94 +377,6 @@ queue: notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:52428800;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" # Kafka properties for Housekeeper tasks topic housekeeper: "${TB_QUEUE_KAFKA_HOUSEKEEPER_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:52428800;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}" - aws_sqs: - # Use the default credentials provider for AWS SQS - use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}" - # Access key ID from AWS IAM user - access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" - # Secret access key from AWS IAM user - secret_access_key: "${TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY:YOUR_SECRET}" - # Region from AWS account - region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}" - # Number of threads per each AWS SQS queue in consumer - threads_per_topic: "${TB_QUEUE_AWS_SQS_THREADS_PER_TOPIC:1}" - # Thread pool size for aws_sqs queue producer executor provider. Default value equals to AmazonSQSAsyncClient.DEFAULT_THREAD_POOL_SIZE - producer_thread_pool_size: "${TB_QUEUE_AWS_SQS_EXECUTOR_THREAD_POOL_SIZE:50}" - queue-properties: - # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - rule-engine: "${TB_QUEUE_AWS_SQS_RE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - pubsub: - # Project ID from Google Cloud - project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}" - # API Credentials in JSON format - service_account: "${TB_QUEUE_PUBSUB_SERVICE_ACCOUNT:YOUR_SERVICE_ACCOUNT}" - # Message size for PubSub queue. Value in bytes - max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" - # Number of messages per consumer - max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" - # Thread pool size for pubsub queue executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:0}" - queue-properties: - # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again - rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - # Pub/Sub properties for Core subscribers, messages which will commit after ackDeadlineInSec period can be consumed again - core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - # Pub/Sub properties for Transport API subscribers, messages which will commit after ackDeadlineInSec period can be consumed again - transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - # Pub/Sub properties for Version Control subscribers, messages which will commit after ackDeadlineInSec period can be consumed again - notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - service_bus: - # Azure namespace - namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}" - # Azure Service Bus Shared Access Signatures key name - sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}" - # Azure Service Bus Shared Access Signatures key - sas_key: "${TB_QUEUE_SERVICE_BUS_SAS_KEY:YOUR_SAS_KEY}" - # Number of messages per a consumer - max_messages: "${TB_QUEUE_SERVICE_BUS_MAX_MESSAGES:1000}" - queue-properties: - # Azure Service Bus properties for Rule Engine queues - rule-engine: "${TB_QUEUE_SERVICE_BUS_RE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - # Azure Service Bus properties for Core queues - core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - # Azure Service Bus properties for Transport Api queues - transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - # Azure Service Bus properties for Notification queues - notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - rabbitmq: - # By default empty - exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}" - # RabbitMQ host used to establish connection - host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}" - # RabbitMQ host used to establish a connection - port: "${TB_QUEUE_RABBIT_MQ_PORT:5672}" - # Virtual hosts provide logical grouping and separation of resources - virtual_host: "${TB_QUEUE_RABBIT_MQ_VIRTUAL_HOST:/}" - # Username for RabbitMQ user account - username: "${TB_QUEUE_RABBIT_MQ_USERNAME:YOUR_USERNAME}" - # User password for RabbitMQ user account - password: "${TB_QUEUE_RABBIT_MQ_PASSWORD:YOUR_PASSWORD}" - # Network connection between clients and RabbitMQ nodes can fail. RabbitMQ Java client supports automatic recovery of connections and topology (queues, exchanges, bindings, and consumers) - automatic_recovery_enabled: "${TB_QUEUE_RABBIT_MQ_AUTOMATIC_RECOVERY_ENABLED:false}" - # The connection timeout for the RabbitMQ connection factory - connection_timeout: "${TB_QUEUE_RABBIT_MQ_CONNECTION_TIMEOUT:60000}" - # RabbitMQ has a timeout for connection handshake. When clients run in heavily constrained environments, it may be necessary to increase the timeout - handshake_timeout: "${TB_QUEUE_RABBIT_MQ_HANDSHAKE_TIMEOUT:10000}" - queue-properties: - # RabbitMQ properties for Rule Engine queues - rule-engine: "${TB_QUEUE_RABBIT_MQ_RE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" - # RabbitMQ properties for Core queues - core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" - # RabbitMQ properties for Transport API queues - transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" - # RabbitMQ properties for Notification queues - notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" partitions: hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 transport_api: @@ -483,7 +395,7 @@ queue: # Interval in milliseconds to poll api response from transport microservices response_poll_interval: "${TB_QUEUE_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}" core: - # Default topic name of Kafka, RabbitMQ, etc. queue + # Default topic name topic: "${TB_QUEUE_CORE_TOPIC:tb_core}" # Interval in milliseconds to poll messages by Core microservices poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}" diff --git a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml index c9fd10a99d..8d9a60a319 100644 --- a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml +++ b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml @@ -235,7 +235,7 @@ transport: # Queue configuration parameters queue: - type: "${TB_QUEUE_TYPE:kafka}" # kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) + type: "${TB_QUEUE_TYPE:kafka}" # kafka (Apache Kafka) prefix: "${TB_QUEUE_PREFIX:}" # Global queue prefix. If specified, prefix is added before default topic name: 'prefix.default_topic_name'. Prefix is applied to all topics (and consumer groups for kafka). kafka: # Kafka Bootstrap Servers @@ -310,94 +310,6 @@ queue: notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:52428800;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" # Kafka properties for Housekeeper tasks topic housekeeper: "${TB_QUEUE_KAFKA_HOUSEKEEPER_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:52428800;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}" - aws_sqs: - # Use the default credentials provider for AWS SQS - use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}" - # Access key ID from AWS IAM user - access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" - # Secret access key from AWS IAM user - secret_access_key: "${TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY:YOUR_SECRET}" - # Region from AWS account - region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}" - # Number of threads per each AWS SQS queue in consumer - threads_per_topic: "${TB_QUEUE_AWS_SQS_THREADS_PER_TOPIC:1}" - # Thread pool size for aws_sqs queue producer executor provider. Default value equals to AmazonSQSAsyncClient.DEFAULT_THREAD_POOL_SIZE - producer_thread_pool_size: "${TB_QUEUE_AWS_SQS_EXECUTOR_THREAD_POOL_SIZE:50}" - queue-properties: - # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - rule-engine: "${TB_QUEUE_AWS_SQS_RE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - pubsub: - # Project ID from Google Cloud - project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}" - # API Credentials in JSON format - service_account: "${TB_QUEUE_PUBSUB_SERVICE_ACCOUNT:YOUR_SERVICE_ACCOUNT}" - # Message size for PubSub queue.Value in bytes - max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" - # Number of messages per consumer - max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" - # Thread pool size for pubsub queue executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:0}" - queue-properties: - # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again - rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - # Pub/Sub properties for Core subscribers, messages which will commit after ackDeadlineInSec period can be consumed again - core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - # Pub/Sub properties for Transport API subscribers, messages which will commit after ackDeadlineInSec period can be consumed again - transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - # Pub/Sub properties for Version Control subscribers, messages which will commit after ackDeadlineInSec period can be consumed again - notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - service_bus: - # Azure namespace - namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}" - # Azure Service Bus Shared Access Signatures key name - sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}" - # Azure Service Bus Shared Access Signatures key - sas_key: "${TB_QUEUE_SERVICE_BUS_SAS_KEY:YOUR_SAS_KEY}" - # Number of messages per a consumer - max_messages: "${TB_QUEUE_SERVICE_BUS_MAX_MESSAGES:1000}" - queue-properties: - # Azure Service Bus properties for Rule Engine queues - rule-engine: "${TB_QUEUE_SERVICE_BUS_RE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - # Azure Service Bus properties for Core queues - core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - # Azure Service Bus properties for Transport Api queues - transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - # Azure Service Bus properties for Notification queues - notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - rabbitmq: - # By default empty - exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}" - # RabbitMQ host used to establish connection - host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}" - # RabbitMQ host used to establish a connection - port: "${TB_QUEUE_RABBIT_MQ_PORT:5672}" - # Virtual hosts provide logical grouping and separation of resources - virtual_host: "${TB_QUEUE_RABBIT_MQ_VIRTUAL_HOST:/}" - # Username for RabbitMQ user account - username: "${TB_QUEUE_RABBIT_MQ_USERNAME:YOUR_USERNAME}" - # User password for RabbitMQ user account - password: "${TB_QUEUE_RABBIT_MQ_PASSWORD:YOUR_PASSWORD}" - # Network connection between clients and RabbitMQ nodes can fail. RabbitMQ Java client supports automatic recovery of connections and topology (queues, exchanges, bindings, and consumers) - automatic_recovery_enabled: "${TB_QUEUE_RABBIT_MQ_AUTOMATIC_RECOVERY_ENABLED:false}" - # The connection timeout for the RabbitMQ connection factory - connection_timeout: "${TB_QUEUE_RABBIT_MQ_CONNECTION_TIMEOUT:60000}" - # RabbitMQ has a timeout for connection handshake. When clients run in heavily constrained environments, it may be necessary to increase the timeout - handshake_timeout: "${TB_QUEUE_RABBIT_MQ_HANDSHAKE_TIMEOUT:10000}" - queue-properties: - # RabbitMQ properties for Rule Engine queues - rule-engine: "${TB_QUEUE_RABBIT_MQ_RE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" - # RabbitMQ properties for Core queues - core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" - # RabbitMQ properties for Transport API queues - transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" - # RabbitMQ properties for Notification queues - notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" partitions: hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 transport_api: @@ -416,7 +328,7 @@ queue: # Interval in milliseconds to poll api response from transport microservices response_poll_interval: "${TB_QUEUE_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}" core: - # Default topic name of Kafka, RabbitMQ, etc. queue + # Default topic name topic: "${TB_QUEUE_CORE_TOPIC:tb_core}" # Interval in milliseconds to poll messages by Core microservices poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}" @@ -424,7 +336,7 @@ queue: partitions: "${TB_QUEUE_CORE_PARTITIONS:10}" # Timeout for processing a message pack by Core microservices pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}" - # Default topic name for queue Kafka, RabbitMQ, etc. + # Default topic name usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}" stats: # Enable/disable statistics for Core microservices diff --git a/transport/snmp/src/main/resources/tb-snmp-transport.yml b/transport/snmp/src/main/resources/tb-snmp-transport.yml index 85a125b351..4c11bd0018 100644 --- a/transport/snmp/src/main/resources/tb-snmp-transport.yml +++ b/transport/snmp/src/main/resources/tb-snmp-transport.yml @@ -181,7 +181,7 @@ transport: # Queue configuration parameters queue: - type: "${TB_QUEUE_TYPE:kafka}" # kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) + type: "${TB_QUEUE_TYPE:kafka}" # kafka (Apache Kafka) prefix: "${TB_QUEUE_PREFIX:}" # Global queue prefix. If specified, prefix is added before default topic name: 'prefix.default_topic_name'. Prefix is applied to all topics (and consumer groups for kafka). kafka: # Kafka Bootstrap Servers @@ -263,94 +263,6 @@ queue: print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}" # Time to wait for the stats-loading requests to Kafka to finis kafka-response-timeout-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_RESPONSE_TIMEOUT_MS:1000}" - aws_sqs: - # Use the default credentials provider for AWS SQS - use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}" - # Access key ID from AWS IAM user - access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" - # Secret access key from AWS IAM user - secret_access_key: "${TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY:YOUR_SECRET}" - # Region from AWS account - region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}" - # Number of threads per each AWS SQS queue in consumer - threads_per_topic: "${TB_QUEUE_AWS_SQS_THREADS_PER_TOPIC:1}" - # Thread pool size for aws_sqs queue producer executor provider. Default value equals to AmazonSQSAsyncClient.DEFAULT_THREAD_POOL_SIZE - producer_thread_pool_size: "${TB_QUEUE_AWS_SQS_EXECUTOR_THREAD_POOL_SIZE:50}" - queue-properties: - # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - rule-engine: "${TB_QUEUE_AWS_SQS_RE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds - notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - pubsub: - # Project ID from Google Cloud - project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}" - # API Credentials in JSON format - service_account: "${TB_QUEUE_PUBSUB_SERVICE_ACCOUNT:YOUR_SERVICE_ACCOUNT}" - # Message size for PubSub queue. Value in bytes - max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" - # Number of messages per consumer - max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" - # Thread pool size for pubsub queue executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:0}" - queue-properties: - # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again - rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - # Pub/Sub properties for Core subscribers, messages which will commit after ackDeadlineInSec period can be consumed again - core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - # Pub/Sub properties for Transport API subscribers, messages which will commit after ackDeadlineInSec period can be consumed again - transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - # Pub/Sub properties for Version Control subscribers, messages which will commit after ackDeadlineInSec period can be consumed again - notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" - service_bus: - # Azure namespace - namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}" - # Azure Service Bus Shared Access Signatures key name - sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}" - # Azure Service Bus Shared Access Signatures key - sas_key: "${TB_QUEUE_SERVICE_BUS_SAS_KEY:YOUR_SAS_KEY}" - # Number of messages per a consumer - max_messages: "${TB_QUEUE_SERVICE_BUS_MAX_MESSAGES:1000}" - queue-properties: - # Azure Service Bus properties for Rule Engine queues - rule-engine: "${TB_QUEUE_SERVICE_BUS_RE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - # Azure Service Bus properties for Core queues - core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - # Azure Service Bus properties for Transport Api queues - transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - # Azure Service Bus properties for Notification queues - notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" - rabbitmq: - # By default empty - exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}" - # RabbitMQ host used to establish connection - host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}" - # RabbitMQ host used to establish a connection - port: "${TB_QUEUE_RABBIT_MQ_PORT:5672}" - # Virtual hosts provide logical grouping and separation of resources - virtual_host: "${TB_QUEUE_RABBIT_MQ_VIRTUAL_HOST:/}" - # Username for RabbitMQ user account - username: "${TB_QUEUE_RABBIT_MQ_USERNAME:YOUR_USERNAME}" - # User password for RabbitMQ user account - password: "${TB_QUEUE_RABBIT_MQ_PASSWORD:YOUR_PASSWORD}" - # Network connection between clients and RabbitMQ nodes can fail. RabbitMQ Java client supports automatic recovery of connections and topology (queues, exchanges, bindings, and consumers) - automatic_recovery_enabled: "${TB_QUEUE_RABBIT_MQ_AUTOMATIC_RECOVERY_ENABLED:false}" - # The connection timeout for the RabbitMQ connection factory - connection_timeout: "${TB_QUEUE_RABBIT_MQ_CONNECTION_TIMEOUT:60000}" - # RabbitMQ has a timeout for connection handshake. When clients run in heavily constrained environments, it may be necessary to increase the timeout - handshake_timeout: "${TB_QUEUE_RABBIT_MQ_HANDSHAKE_TIMEOUT:10000}" - queue-properties: - # RabbitMQ properties for Rule Engine queues - rule-engine: "${TB_QUEUE_RABBIT_MQ_RE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" - # RabbitMQ properties for Core queues - core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" - # RabbitMQ properties for Transport API queues - transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" - # RabbitMQ properties for Notification queues - notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" partitions: hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 transport_api: @@ -369,7 +281,7 @@ queue: # Interval in milliseconds to poll api response from transport microservices response_poll_interval: "${TB_QUEUE_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}" core: - # Default topic name of Kafka, RabbitMQ, etc. queue + # Default topic name topic: "${TB_QUEUE_CORE_TOPIC:tb_core}" # Interval in milliseconds to poll messages by Core microservices poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}" @@ -377,7 +289,7 @@ queue: partitions: "${TB_QUEUE_CORE_PARTITIONS:10}" # Timeout for processing a message pack by Core microservices pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}" - # Stats topic name for queue Kafka, RabbitMQ, etc. + # Stats topic name usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}" stats: # Enable/disable statistics for Core microservices From 8ac0468eddea77f0986cb74dff335964aa3af3a8 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 4 Dec 2024 16:51:03 +0200 Subject: [PATCH 002/108] Version set to 4.0.0-SNAPSHOT --- application/pom.xml | 2 +- application/src/main/resources/thingsboard.yml | 2 +- common/actor/pom.xml | 2 +- common/cache/pom.xml | 2 +- common/cluster-api/pom.xml | 2 +- common/coap-server/pom.xml | 2 +- common/dao-api/pom.xml | 2 +- common/data/pom.xml | 2 +- common/edge-api/pom.xml | 2 +- common/message/pom.xml | 2 +- common/pom.xml | 2 +- common/proto/pom.xml | 2 +- common/queue/pom.xml | 2 +- common/script/pom.xml | 2 +- common/script/remote-js-client/pom.xml | 2 +- common/script/script-api/pom.xml | 2 +- common/stats/pom.xml | 2 +- common/transport/coap/pom.xml | 2 +- common/transport/http/pom.xml | 2 +- common/transport/lwm2m/pom.xml | 2 +- common/transport/mqtt/pom.xml | 2 +- common/transport/pom.xml | 2 +- common/transport/snmp/pom.xml | 2 +- common/transport/transport-api/pom.xml | 2 +- common/util/pom.xml | 2 +- common/version-control/pom.xml | 2 +- dao/pom.xml | 2 +- monitoring/pom.xml | 2 +- msa/black-box-tests/pom.xml | 2 +- msa/js-executor/package.json | 2 +- msa/js-executor/pom.xml | 2 +- msa/monitoring/pom.xml | 2 +- msa/pom.xml | 2 +- msa/tb-node/pom.xml | 2 +- msa/tb/docker/install-tb.sh | 3 --- msa/tb/docker/upgrade-tb.sh | 13 ------------- msa/tb/pom.xml | 3 +-- msa/transport/coap/pom.xml | 2 +- msa/transport/http/pom.xml | 2 +- msa/transport/lwm2m/pom.xml | 2 +- msa/transport/mqtt/pom.xml | 2 +- msa/transport/pom.xml | 2 +- msa/transport/snmp/pom.xml | 2 +- msa/vc-executor-docker/pom.xml | 2 +- msa/vc-executor/pom.xml | 2 +- msa/web-ui/package.json | 2 +- msa/web-ui/pom.xml | 2 +- netty-mqtt/pom.xml | 4 ++-- pom.xml | 2 +- rest-client/pom.xml | 2 +- rule-engine/pom.xml | 2 +- rule-engine/rule-engine-api/pom.xml | 2 +- rule-engine/rule-engine-components/pom.xml | 2 +- tools/pom.xml | 2 +- transport/coap/pom.xml | 2 +- transport/http/pom.xml | 2 +- transport/lwm2m/pom.xml | 2 +- transport/mqtt/pom.xml | 2 +- transport/pom.xml | 2 +- transport/snmp/pom.xml | 2 +- ui-ngx/package.json | 2 +- ui-ngx/pom.xml | 2 +- 62 files changed, 61 insertions(+), 78 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index 17b179c767..112cc82ff7 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT thingsboard application diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index f642d7fea5..ef6d2204d8 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -206,7 +206,7 @@ ui: # Help parameters help: # Base URL for UI help assets - base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-3.9}" + base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-4.0}" # Database telemetry parameters database: diff --git a/common/actor/pom.xml b/common/actor/pom.xml index cc19cc949f..9b0053e0b6 100644 --- a/common/actor/pom.xml +++ b/common/actor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/cache/pom.xml b/common/cache/pom.xml index 1ecb95c6c5..fd9802a689 100644 --- a/common/cache/pom.xml +++ b/common/cache/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/cluster-api/pom.xml b/common/cluster-api/pom.xml index bd7dc214cf..d53ac4904d 100644 --- a/common/cluster-api/pom.xml +++ b/common/cluster-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/coap-server/pom.xml b/common/coap-server/pom.xml index 401d1e94fd..30001c929d 100644 --- a/common/coap-server/pom.xml +++ b/common/coap-server/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml index 62c1e420c2..0bd0f476ec 100644 --- a/common/dao-api/pom.xml +++ b/common/dao-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/data/pom.xml b/common/data/pom.xml index b206f0b173..f7c559c728 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml index 31d90de948..4f0fe5aac0 100644 --- a/common/edge-api/pom.xml +++ b/common/edge-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/message/pom.xml b/common/message/pom.xml index ac56095e7e..0e290d1e01 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/pom.xml b/common/pom.xml index c905e0eee9..4c8f56cbce 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT thingsboard common diff --git a/common/proto/pom.xml b/common/proto/pom.xml index 4409d6b97f..9a9b262d82 100644 --- a/common/proto/pom.xml +++ b/common/proto/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/queue/pom.xml b/common/queue/pom.xml index 7485294f6e..6fad5efd5a 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/script/pom.xml b/common/script/pom.xml index 88e35e8429..8c4ea360cf 100644 --- a/common/script/pom.xml +++ b/common/script/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/script/remote-js-client/pom.xml b/common/script/remote-js-client/pom.xml index 7309e6af51..2b780bf489 100644 --- a/common/script/remote-js-client/pom.xml +++ b/common/script/remote-js-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT script org.thingsboard.common.script diff --git a/common/script/script-api/pom.xml b/common/script/script-api/pom.xml index 08ef60713e..ed93701513 100644 --- a/common/script/script-api/pom.xml +++ b/common/script/script-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT script org.thingsboard.common.script diff --git a/common/stats/pom.xml b/common/stats/pom.xml index 31fbc72dcc..7cc67d5821 100644 --- a/common/stats/pom.xml +++ b/common/stats/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml index d4528944e0..86459e9821 100644 --- a/common/transport/coap/pom.xml +++ b/common/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml index 054fc3cbbe..8ab2243ceb 100644 --- a/common/transport/http/pom.xml +++ b/common/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/lwm2m/pom.xml b/common/transport/lwm2m/pom.xml index 54232ac104..a6e7209f21 100644 --- a/common/transport/lwm2m/pom.xml +++ b/common/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml index e672f7a5dd..d074afc6b5 100644 --- a/common/transport/mqtt/pom.xml +++ b/common/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/pom.xml b/common/transport/pom.xml index c87fa8c2bf..fdd3ad21d5 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml index 78665bfde2..5d4d37547f 100644 --- a/common/transport/snmp/pom.xml +++ b/common/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.common - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index 7d221c48bf..e2f03b060a 100644 --- a/common/transport/transport-api/pom.xml +++ b/common/transport/transport-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/util/pom.xml b/common/util/pom.xml index ad3bbaba58..dc75047267 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/version-control/pom.xml b/common/version-control/pom.xml index f37139b3c5..ea568dfcd9 100644 --- a/common/version-control/pom.xml +++ b/common/version-control/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/dao/pom.xml b/dao/pom.xml index 1dac54c142..5021742154 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT thingsboard dao diff --git a/monitoring/pom.xml b/monitoring/pom.xml index 013a0b5c47..d30a9ca71f 100644 --- a/monitoring/pom.xml +++ b/monitoring/pom.xml @@ -21,7 +21,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT thingsboard diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml index 4d34a13f38..c1c0980b64 100644 --- a/msa/black-box-tests/pom.xml +++ b/msa/black-box-tests/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/js-executor/package.json b/msa/js-executor/package.json index 0fb241c558..a29dc1ef54 100644 --- a/msa/js-executor/package.json +++ b/msa/js-executor/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard-js-executor", "private": true, - "version": "3.9.0", + "version": "4.0.0", "description": "ThingsBoard JavaScript Executor Microservice", "main": "server.ts", "bin": "server.js", diff --git a/msa/js-executor/pom.xml b/msa/js-executor/pom.xml index f747db1438..62545f5ad6 100644 --- a/msa/js-executor/pom.xml +++ b/msa/js-executor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/monitoring/pom.xml b/msa/monitoring/pom.xml index 3a4834236c..1b03765586 100644 --- a/msa/monitoring/pom.xml +++ b/msa/monitoring/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT msa diff --git a/msa/pom.xml b/msa/pom.xml index d77384cc74..ff394295f4 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT thingsboard msa diff --git a/msa/tb-node/pom.xml b/msa/tb-node/pom.xml index 11295dc4b9..6142c3c5d8 100644 --- a/msa/tb-node/pom.xml +++ b/msa/tb-node/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/tb/docker/install-tb.sh b/msa/tb/docker/install-tb.sh index 9567908316..b926aac0bb 100644 --- a/msa/tb/docker/install-tb.sh +++ b/msa/tb/docker/install-tb.sh @@ -40,7 +40,6 @@ fi CONF_FOLDER="${pkg.installFolder}/conf" jarfile=${pkg.installFolder}/bin/${pkg.name}.jar configfile=${pkg.name}.conf -upgradeversion=${DATA_FOLDER}/.upgradeversion source "${CONF_FOLDER}/${configfile}" @@ -54,5 +53,3 @@ java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.ThingsboardI -Dinstall.upgrade=false \ -Dlogging.config=/usr/share/thingsboard/bin/install/logback.xml \ org.springframework.boot.loader.launch.PropertiesLauncher - -echo "${pkg.upgradeVersion}" > ${upgradeversion} diff --git a/msa/tb/docker/upgrade-tb.sh b/msa/tb/docker/upgrade-tb.sh index 545bf4b8d6..3c8e4f194d 100644 --- a/msa/tb/docker/upgrade-tb.sh +++ b/msa/tb/docker/upgrade-tb.sh @@ -20,28 +20,15 @@ start-db.sh CONF_FOLDER="${pkg.installFolder}/conf" jarfile=${pkg.installFolder}/bin/${pkg.name}.jar configfile=${pkg.name}.conf -upgradeversion=${DATA_FOLDER}/.upgradeversion source "${CONF_FOLDER}/${configfile}" -FROM_VERSION=`cat ${upgradeversion}` - echo "Starting ThingsBoard upgrade ..." -if [[ -z "${FROM_VERSION// }" ]]; then - echo "FROM_VERSION variable is invalid or unspecified!" - exit 1 -else - fromVersion="${FROM_VERSION// }" -fi - java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.ThingsboardInstallApplication \ -Dspring.jpa.hibernate.ddl-auto=none \ -Dinstall.upgrade=true \ - -Dinstall.upgrade.from_version=${fromVersion} \ -Dlogging.config=/usr/share/thingsboard/bin/install/logback.xml \ org.springframework.boot.loader.launch.PropertiesLauncher -echo "${pkg.upgradeVersion}" > ${upgradeversion} - stop-db.sh \ No newline at end of file diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml index 79a2a66385..0c6046c09e 100644 --- a/msa/tb/pom.xml +++ b/msa/tb/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT msa org.thingsboard.msa @@ -38,7 +38,6 @@ tb-postgres tb-cassandra /usr/share/${pkg.name} - 3.9.0 diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml index 9ec8589d9a..325d4d45fa 100644 --- a/msa/transport/coap/pom.xml +++ b/msa/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml index d562f89cd5..945c523455 100644 --- a/msa/transport/http/pom.xml +++ b/msa/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/lwm2m/pom.xml b/msa/transport/lwm2m/pom.xml index 992187316a..4d0370c1cf 100644 --- a/msa/transport/lwm2m/pom.xml +++ b/msa/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml index 32aa6a4f23..20791ad61c 100644 --- a/msa/transport/mqtt/pom.xml +++ b/msa/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml index 34be0bd338..0b92e1fa97 100644 --- a/msa/transport/pom.xml +++ b/msa/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/transport/snmp/pom.xml b/msa/transport/snmp/pom.xml index 95919ad175..b16469523e 100644 --- a/msa/transport/snmp/pom.xml +++ b/msa/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.msa transport - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT org.thingsboard.msa.transport diff --git a/msa/vc-executor-docker/pom.xml b/msa/vc-executor-docker/pom.xml index 8aeeed2b1e..72995b05ab 100644 --- a/msa/vc-executor-docker/pom.xml +++ b/msa/vc-executor-docker/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/vc-executor/pom.xml b/msa/vc-executor/pom.xml index 0932bf7c06..1b88a41692 100644 --- a/msa/vc-executor/pom.xml +++ b/msa/vc-executor/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/web-ui/package.json b/msa/web-ui/package.json index 84b1a4a488..3374112096 100644 --- a/msa/web-ui/package.json +++ b/msa/web-ui/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard-web-ui", "private": true, - "version": "3.9.0", + "version": "4.0.0", "description": "ThingsBoard Web UI Microservice", "main": "server.ts", "bin": "server.js", diff --git a/msa/web-ui/pom.xml b/msa/web-ui/pom.xml index 195ae123f4..361487b516 100644 --- a/msa/web-ui/pom.xml +++ b/msa/web-ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index 7de766b2da..f7361a686d 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -19,11 +19,11 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT thingsboard netty-mqtt - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT jar Netty MQTT Client diff --git a/pom.xml b/pom.xml index 8dc3e1c40a..0135a1b4ea 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT pom Thingsboard diff --git a/rest-client/pom.xml b/rest-client/pom.xml index 069f7b84f0..c8232512f7 100644 --- a/rest-client/pom.xml +++ b/rest-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT thingsboard rest-client diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml index e6abae70ef..bd14daf22e 100644 --- a/rule-engine/pom.xml +++ b/rule-engine/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT thingsboard rule-engine diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml index 57bc5771d2..886a2169f3 100644 --- a/rule-engine/rule-engine-api/pom.xml +++ b/rule-engine/rule-engine-api/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index 99853b0950..750701894f 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/tools/pom.xml b/tools/pom.xml index 2d196cec7d..4f7c3f48a6 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT thingsboard tools diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index 76655178ae..452ef6ec9a 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/http/pom.xml b/transport/http/pom.xml index 7e569ea1f1..2408340792 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/lwm2m/pom.xml b/transport/lwm2m/pom.xml index b5b7867146..3ba43f5b53 100644 --- a/transport/lwm2m/pom.xml +++ b/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index 88009474aa..e31d618890 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/pom.xml b/transport/pom.xml index 8e7f77be9b..dd1c415e6c 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT thingsboard transport diff --git a/transport/snmp/pom.xml b/transport/snmp/pom.xml index 02fe36f657..08f23d2c2d 100644 --- a/transport/snmp/pom.xml +++ b/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport diff --git a/ui-ngx/package.json b/ui-ngx/package.json index 0eff3bbd8d..d8870a3568 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -1,6 +1,6 @@ { "name": "thingsboard", - "version": "3.9.0", + "version": "4.0.0", "scripts": { "ng": "ng", "start": "node --max_old_space_size=8048 ./node_modules/@angular/cli/bin/ng serve --configuration development --host 0.0.0.0 --open", diff --git a/ui-ngx/pom.xml b/ui-ngx/pom.xml index f16e352d71..9515f3b3ec 100644 --- a/ui-ngx/pom.xml +++ b/ui-ngx/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT thingsboard org.thingsboard From 190bba72f9916d1a93022206702b485f8f90f610 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 4 Dec 2024 16:52:14 +0200 Subject: [PATCH 003/108] Temp Version set to 3.9.0-RC --- application/pom.xml | 2 +- common/actor/pom.xml | 2 +- common/cache/pom.xml | 2 +- common/cluster-api/pom.xml | 2 +- common/coap-server/pom.xml | 2 +- common/dao-api/pom.xml | 2 +- common/data/pom.xml | 2 +- common/edge-api/pom.xml | 2 +- common/message/pom.xml | 2 +- common/pom.xml | 2 +- common/proto/pom.xml | 2 +- common/queue/pom.xml | 2 +- common/script/pom.xml | 2 +- common/script/remote-js-client/pom.xml | 2 +- common/script/script-api/pom.xml | 2 +- common/stats/pom.xml | 2 +- common/transport/coap/pom.xml | 2 +- common/transport/http/pom.xml | 2 +- common/transport/lwm2m/pom.xml | 2 +- common/transport/mqtt/pom.xml | 2 +- common/transport/pom.xml | 2 +- common/transport/snmp/pom.xml | 2 +- common/transport/transport-api/pom.xml | 2 +- common/util/pom.xml | 2 +- common/version-control/pom.xml | 2 +- dao/pom.xml | 2 +- monitoring/pom.xml | 2 +- msa/black-box-tests/pom.xml | 2 +- msa/js-executor/pom.xml | 2 +- msa/monitoring/pom.xml | 2 +- msa/pom.xml | 2 +- msa/tb-node/pom.xml | 2 +- msa/tb/pom.xml | 2 +- msa/transport/coap/pom.xml | 2 +- msa/transport/http/pom.xml | 2 +- msa/transport/lwm2m/pom.xml | 2 +- msa/transport/mqtt/pom.xml | 2 +- msa/transport/pom.xml | 2 +- msa/transport/snmp/pom.xml | 2 +- msa/vc-executor-docker/pom.xml | 2 +- msa/vc-executor/pom.xml | 2 +- msa/web-ui/pom.xml | 2 +- netty-mqtt/pom.xml | 4 ++-- pom.xml | 2 +- rest-client/pom.xml | 2 +- rule-engine/pom.xml | 2 +- rule-engine/rule-engine-api/pom.xml | 2 +- rule-engine/rule-engine-components/pom.xml | 2 +- tools/pom.xml | 2 +- transport/coap/pom.xml | 2 +- transport/http/pom.xml | 2 +- transport/lwm2m/pom.xml | 2 +- transport/mqtt/pom.xml | 2 +- transport/pom.xml | 2 +- transport/snmp/pom.xml | 2 +- ui-ngx/pom.xml | 2 +- 56 files changed, 57 insertions(+), 57 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index 112cc82ff7..ef57037448 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC thingsboard application diff --git a/common/actor/pom.xml b/common/actor/pom.xml index 9b0053e0b6..cbbce12336 100644 --- a/common/actor/pom.xml +++ b/common/actor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/cache/pom.xml b/common/cache/pom.xml index fd9802a689..a4f7dd4f7b 100644 --- a/common/cache/pom.xml +++ b/common/cache/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/cluster-api/pom.xml b/common/cluster-api/pom.xml index d53ac4904d..4bbe65d99f 100644 --- a/common/cluster-api/pom.xml +++ b/common/cluster-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/coap-server/pom.xml b/common/coap-server/pom.xml index 30001c929d..32da47ca08 100644 --- a/common/coap-server/pom.xml +++ b/common/coap-server/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml index 0bd0f476ec..7d48a63903 100644 --- a/common/dao-api/pom.xml +++ b/common/dao-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/data/pom.xml b/common/data/pom.xml index f7c559c728..e6eca1aa93 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml index 4f0fe5aac0..68e647642d 100644 --- a/common/edge-api/pom.xml +++ b/common/edge-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/message/pom.xml b/common/message/pom.xml index 0e290d1e01..0614c94bc4 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/pom.xml b/common/pom.xml index 4c8f56cbce..8bd1904d72 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC thingsboard common diff --git a/common/proto/pom.xml b/common/proto/pom.xml index 9a9b262d82..fded7298e4 100644 --- a/common/proto/pom.xml +++ b/common/proto/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/queue/pom.xml b/common/queue/pom.xml index 6fad5efd5a..fab4ff3989 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/script/pom.xml b/common/script/pom.xml index 8c4ea360cf..1aad1dd39e 100644 --- a/common/script/pom.xml +++ b/common/script/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/script/remote-js-client/pom.xml b/common/script/remote-js-client/pom.xml index 2b780bf489..38a14cd8f3 100644 --- a/common/script/remote-js-client/pom.xml +++ b/common/script/remote-js-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-SNAPSHOT + 3.9.0-RC script org.thingsboard.common.script diff --git a/common/script/script-api/pom.xml b/common/script/script-api/pom.xml index ed93701513..4210f1f528 100644 --- a/common/script/script-api/pom.xml +++ b/common/script/script-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-SNAPSHOT + 3.9.0-RC script org.thingsboard.common.script diff --git a/common/stats/pom.xml b/common/stats/pom.xml index 7cc67d5821..231811144a 100644 --- a/common/stats/pom.xml +++ b/common/stats/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml index 86459e9821..0bd4c0d6e3 100644 --- a/common/transport/coap/pom.xml +++ b/common/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.common.transport diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml index 8ab2243ceb..5a0dd9e4ff 100644 --- a/common/transport/http/pom.xml +++ b/common/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.common.transport diff --git a/common/transport/lwm2m/pom.xml b/common/transport/lwm2m/pom.xml index a6e7209f21..ddb9d7a399 100644 --- a/common/transport/lwm2m/pom.xml +++ b/common/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.common.transport diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml index d074afc6b5..45d228f225 100644 --- a/common/transport/mqtt/pom.xml +++ b/common/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.common.transport diff --git a/common/transport/pom.xml b/common/transport/pom.xml index fdd3ad21d5..6c4310151d 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml index 5d4d37547f..a2b9dd7c84 100644 --- a/common/transport/snmp/pom.xml +++ b/common/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.common - 4.0.0-SNAPSHOT + 3.9.0-RC transport diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index e2f03b060a..8f01cab08b 100644 --- a/common/transport/transport-api/pom.xml +++ b/common/transport/transport-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.common.transport diff --git a/common/util/pom.xml b/common/util/pom.xml index dc75047267..194724b70f 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/version-control/pom.xml b/common/version-control/pom.xml index ea568dfcd9..8e3fceff99 100644 --- a/common/version-control/pom.xml +++ b/common/version-control/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/dao/pom.xml b/dao/pom.xml index 5021742154..c1f5a1f62b 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC thingsboard dao diff --git a/monitoring/pom.xml b/monitoring/pom.xml index d30a9ca71f..e19acb0fe6 100644 --- a/monitoring/pom.xml +++ b/monitoring/pom.xml @@ -21,7 +21,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC thingsboard diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml index c1c0980b64..5563fcc8d8 100644 --- a/msa/black-box-tests/pom.xml +++ b/msa/black-box-tests/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC msa org.thingsboard.msa diff --git a/msa/js-executor/pom.xml b/msa/js-executor/pom.xml index 62545f5ad6..daea52bfa5 100644 --- a/msa/js-executor/pom.xml +++ b/msa/js-executor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC msa org.thingsboard.msa diff --git a/msa/monitoring/pom.xml b/msa/monitoring/pom.xml index 1b03765586..20f8d90695 100644 --- a/msa/monitoring/pom.xml +++ b/msa/monitoring/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC msa diff --git a/msa/pom.xml b/msa/pom.xml index ff394295f4..057f37a575 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC thingsboard msa diff --git a/msa/tb-node/pom.xml b/msa/tb-node/pom.xml index 6142c3c5d8..3ca22bcf55 100644 --- a/msa/tb-node/pom.xml +++ b/msa/tb-node/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC msa org.thingsboard.msa diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml index 0c6046c09e..5e79740c8b 100644 --- a/msa/tb/pom.xml +++ b/msa/tb/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC msa org.thingsboard.msa diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml index 325d4d45fa..089581f1cb 100644 --- a/msa/transport/coap/pom.xml +++ b/msa/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.0.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.msa.transport diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml index 945c523455..4d9e17df5f 100644 --- a/msa/transport/http/pom.xml +++ b/msa/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.0.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.msa.transport diff --git a/msa/transport/lwm2m/pom.xml b/msa/transport/lwm2m/pom.xml index 4d0370c1cf..94b86d035f 100644 --- a/msa/transport/lwm2m/pom.xml +++ b/msa/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.0.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.msa.transport diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml index 20791ad61c..2b637ddd19 100644 --- a/msa/transport/mqtt/pom.xml +++ b/msa/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.0.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.msa.transport diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml index 0b92e1fa97..4f7e0ad030 100644 --- a/msa/transport/pom.xml +++ b/msa/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC msa org.thingsboard.msa diff --git a/msa/transport/snmp/pom.xml b/msa/transport/snmp/pom.xml index b16469523e..94bc03f29d 100644 --- a/msa/transport/snmp/pom.xml +++ b/msa/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.msa transport - 4.0.0-SNAPSHOT + 3.9.0-RC org.thingsboard.msa.transport diff --git a/msa/vc-executor-docker/pom.xml b/msa/vc-executor-docker/pom.xml index 72995b05ab..f6dc43ea13 100644 --- a/msa/vc-executor-docker/pom.xml +++ b/msa/vc-executor-docker/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC msa org.thingsboard.msa diff --git a/msa/vc-executor/pom.xml b/msa/vc-executor/pom.xml index 1b88a41692..725c3e40ad 100644 --- a/msa/vc-executor/pom.xml +++ b/msa/vc-executor/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC msa org.thingsboard.msa diff --git a/msa/web-ui/pom.xml b/msa/web-ui/pom.xml index 361487b516..c5a900e004 100644 --- a/msa/web-ui/pom.xml +++ b/msa/web-ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC msa org.thingsboard.msa diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index f7361a686d..d4cd55629d 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -19,11 +19,11 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC thingsboard netty-mqtt - 4.0.0-SNAPSHOT + 3.9.0-RC jar Netty MQTT Client diff --git a/pom.xml b/pom.xml index 0135a1b4ea..094c48fd5d 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC pom Thingsboard diff --git a/rest-client/pom.xml b/rest-client/pom.xml index c8232512f7..f868acabf5 100644 --- a/rest-client/pom.xml +++ b/rest-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC thingsboard rest-client diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml index bd14daf22e..fbefcd1de9 100644 --- a/rule-engine/pom.xml +++ b/rule-engine/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC thingsboard rule-engine diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml index 886a2169f3..3ee6496452 100644 --- a/rule-engine/rule-engine-api/pom.xml +++ b/rule-engine/rule-engine-api/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC rule-engine org.thingsboard.rule-engine diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index 750701894f..33bb15af00 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC rule-engine org.thingsboard.rule-engine diff --git a/tools/pom.xml b/tools/pom.xml index 4f7c3f48a6..dce8e1606d 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC thingsboard tools diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index 452ef6ec9a..eef23eae9b 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.transport diff --git a/transport/http/pom.xml b/transport/http/pom.xml index 2408340792..d841c56da7 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.transport diff --git a/transport/lwm2m/pom.xml b/transport/lwm2m/pom.xml index 3ba43f5b53..cec30fcbc5 100644 --- a/transport/lwm2m/pom.xml +++ b/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.transport diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index e31d618890..edf3727320 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.transport diff --git a/transport/pom.xml b/transport/pom.xml index dd1c415e6c..d744f7eb2f 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC thingsboard transport diff --git a/transport/snmp/pom.xml b/transport/snmp/pom.xml index 08f23d2c2d..dcd08c10af 100644 --- a/transport/snmp/pom.xml +++ b/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC transport diff --git a/ui-ngx/pom.xml b/ui-ngx/pom.xml index 9515f3b3ec..1384b1c3e6 100644 --- a/ui-ngx/pom.xml +++ b/ui-ngx/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC thingsboard org.thingsboard From 77d2826f9637f791efff9b127fab3dc24549ffe5 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 4 Dec 2024 16:53:19 +0200 Subject: [PATCH 004/108] Version set to 4.0.0-SNAPSHOT --- application/pom.xml | 2 +- common/actor/pom.xml | 2 +- common/cache/pom.xml | 2 +- common/cluster-api/pom.xml | 2 +- common/coap-server/pom.xml | 2 +- common/dao-api/pom.xml | 2 +- common/data/pom.xml | 2 +- common/edge-api/pom.xml | 2 +- common/message/pom.xml | 2 +- common/pom.xml | 2 +- common/proto/pom.xml | 2 +- common/queue/pom.xml | 2 +- common/script/pom.xml | 2 +- common/script/remote-js-client/pom.xml | 2 +- common/script/script-api/pom.xml | 2 +- common/stats/pom.xml | 2 +- common/transport/coap/pom.xml | 2 +- common/transport/http/pom.xml | 2 +- common/transport/lwm2m/pom.xml | 2 +- common/transport/mqtt/pom.xml | 2 +- common/transport/pom.xml | 2 +- common/transport/snmp/pom.xml | 2 +- common/transport/transport-api/pom.xml | 2 +- common/util/pom.xml | 2 +- common/version-control/pom.xml | 2 +- dao/pom.xml | 2 +- monitoring/pom.xml | 2 +- msa/black-box-tests/pom.xml | 2 +- msa/js-executor/pom.xml | 2 +- msa/monitoring/pom.xml | 2 +- msa/pom.xml | 2 +- msa/tb-node/pom.xml | 2 +- msa/tb/pom.xml | 2 +- msa/transport/coap/pom.xml | 2 +- msa/transport/http/pom.xml | 2 +- msa/transport/lwm2m/pom.xml | 2 +- msa/transport/mqtt/pom.xml | 2 +- msa/transport/pom.xml | 2 +- msa/transport/snmp/pom.xml | 2 +- msa/vc-executor-docker/pom.xml | 2 +- msa/vc-executor/pom.xml | 2 +- msa/web-ui/pom.xml | 2 +- netty-mqtt/pom.xml | 4 ++-- pom.xml | 2 +- rest-client/pom.xml | 2 +- rule-engine/pom.xml | 2 +- rule-engine/rule-engine-api/pom.xml | 2 +- rule-engine/rule-engine-components/pom.xml | 2 +- tools/pom.xml | 2 +- transport/coap/pom.xml | 2 +- transport/http/pom.xml | 2 +- transport/lwm2m/pom.xml | 2 +- transport/mqtt/pom.xml | 2 +- transport/pom.xml | 2 +- transport/snmp/pom.xml | 2 +- ui-ngx/pom.xml | 2 +- 56 files changed, 57 insertions(+), 57 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index ef57037448..112cc82ff7 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT thingsboard application diff --git a/common/actor/pom.xml b/common/actor/pom.xml index cbbce12336..9b0053e0b6 100644 --- a/common/actor/pom.xml +++ b/common/actor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/cache/pom.xml b/common/cache/pom.xml index a4f7dd4f7b..fd9802a689 100644 --- a/common/cache/pom.xml +++ b/common/cache/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/cluster-api/pom.xml b/common/cluster-api/pom.xml index 4bbe65d99f..d53ac4904d 100644 --- a/common/cluster-api/pom.xml +++ b/common/cluster-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/coap-server/pom.xml b/common/coap-server/pom.xml index 32da47ca08..30001c929d 100644 --- a/common/coap-server/pom.xml +++ b/common/coap-server/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml index 7d48a63903..0bd0f476ec 100644 --- a/common/dao-api/pom.xml +++ b/common/dao-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/data/pom.xml b/common/data/pom.xml index e6eca1aa93..f7c559c728 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml index 68e647642d..4f0fe5aac0 100644 --- a/common/edge-api/pom.xml +++ b/common/edge-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/message/pom.xml b/common/message/pom.xml index 0614c94bc4..0e290d1e01 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/pom.xml b/common/pom.xml index 8bd1904d72..4c8f56cbce 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT thingsboard common diff --git a/common/proto/pom.xml b/common/proto/pom.xml index fded7298e4..9a9b262d82 100644 --- a/common/proto/pom.xml +++ b/common/proto/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/queue/pom.xml b/common/queue/pom.xml index fab4ff3989..6fad5efd5a 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/script/pom.xml b/common/script/pom.xml index 1aad1dd39e..8c4ea360cf 100644 --- a/common/script/pom.xml +++ b/common/script/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/script/remote-js-client/pom.xml b/common/script/remote-js-client/pom.xml index 38a14cd8f3..2b780bf489 100644 --- a/common/script/remote-js-client/pom.xml +++ b/common/script/remote-js-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-RC + 4.0.0-SNAPSHOT script org.thingsboard.common.script diff --git a/common/script/script-api/pom.xml b/common/script/script-api/pom.xml index 4210f1f528..ed93701513 100644 --- a/common/script/script-api/pom.xml +++ b/common/script/script-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-RC + 4.0.0-SNAPSHOT script org.thingsboard.common.script diff --git a/common/stats/pom.xml b/common/stats/pom.xml index 231811144a..7cc67d5821 100644 --- a/common/stats/pom.xml +++ b/common/stats/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml index 0bd4c0d6e3..86459e9821 100644 --- a/common/transport/coap/pom.xml +++ b/common/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml index 5a0dd9e4ff..8ab2243ceb 100644 --- a/common/transport/http/pom.xml +++ b/common/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/lwm2m/pom.xml b/common/transport/lwm2m/pom.xml index ddb9d7a399..a6e7209f21 100644 --- a/common/transport/lwm2m/pom.xml +++ b/common/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml index 45d228f225..d074afc6b5 100644 --- a/common/transport/mqtt/pom.xml +++ b/common/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/pom.xml b/common/transport/pom.xml index 6c4310151d..fdd3ad21d5 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml index a2b9dd7c84..5d4d37547f 100644 --- a/common/transport/snmp/pom.xml +++ b/common/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.common - 3.9.0-RC + 4.0.0-SNAPSHOT transport diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index 8f01cab08b..e2f03b060a 100644 --- a/common/transport/transport-api/pom.xml +++ b/common/transport/transport-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/util/pom.xml b/common/util/pom.xml index 194724b70f..dc75047267 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/version-control/pom.xml b/common/version-control/pom.xml index 8e3fceff99..ea568dfcd9 100644 --- a/common/version-control/pom.xml +++ b/common/version-control/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/dao/pom.xml b/dao/pom.xml index c1f5a1f62b..5021742154 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT thingsboard dao diff --git a/monitoring/pom.xml b/monitoring/pom.xml index e19acb0fe6..d30a9ca71f 100644 --- a/monitoring/pom.xml +++ b/monitoring/pom.xml @@ -21,7 +21,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT thingsboard diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml index 5563fcc8d8..c1c0980b64 100644 --- a/msa/black-box-tests/pom.xml +++ b/msa/black-box-tests/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/js-executor/pom.xml b/msa/js-executor/pom.xml index daea52bfa5..62545f5ad6 100644 --- a/msa/js-executor/pom.xml +++ b/msa/js-executor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/monitoring/pom.xml b/msa/monitoring/pom.xml index 20f8d90695..1b03765586 100644 --- a/msa/monitoring/pom.xml +++ b/msa/monitoring/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT msa diff --git a/msa/pom.xml b/msa/pom.xml index 057f37a575..ff394295f4 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT thingsboard msa diff --git a/msa/tb-node/pom.xml b/msa/tb-node/pom.xml index 3ca22bcf55..6142c3c5d8 100644 --- a/msa/tb-node/pom.xml +++ b/msa/tb-node/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml index 5e79740c8b..0c6046c09e 100644 --- a/msa/tb/pom.xml +++ b/msa/tb/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml index 089581f1cb..325d4d45fa 100644 --- a/msa/transport/coap/pom.xml +++ b/msa/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.9.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml index 4d9e17df5f..945c523455 100644 --- a/msa/transport/http/pom.xml +++ b/msa/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.9.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/lwm2m/pom.xml b/msa/transport/lwm2m/pom.xml index 94b86d035f..4d0370c1cf 100644 --- a/msa/transport/lwm2m/pom.xml +++ b/msa/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.9.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml index 2b637ddd19..20791ad61c 100644 --- a/msa/transport/mqtt/pom.xml +++ b/msa/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.9.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml index 4f7e0ad030..0b92e1fa97 100644 --- a/msa/transport/pom.xml +++ b/msa/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/transport/snmp/pom.xml b/msa/transport/snmp/pom.xml index 94bc03f29d..b16469523e 100644 --- a/msa/transport/snmp/pom.xml +++ b/msa/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.msa transport - 3.9.0-RC + 4.0.0-SNAPSHOT org.thingsboard.msa.transport diff --git a/msa/vc-executor-docker/pom.xml b/msa/vc-executor-docker/pom.xml index f6dc43ea13..72995b05ab 100644 --- a/msa/vc-executor-docker/pom.xml +++ b/msa/vc-executor-docker/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/vc-executor/pom.xml b/msa/vc-executor/pom.xml index 725c3e40ad..1b88a41692 100644 --- a/msa/vc-executor/pom.xml +++ b/msa/vc-executor/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/web-ui/pom.xml b/msa/web-ui/pom.xml index c5a900e004..361487b516 100644 --- a/msa/web-ui/pom.xml +++ b/msa/web-ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index d4cd55629d..f7361a686d 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -19,11 +19,11 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT thingsboard netty-mqtt - 3.9.0-RC + 4.0.0-SNAPSHOT jar Netty MQTT Client diff --git a/pom.xml b/pom.xml index 094c48fd5d..0135a1b4ea 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT pom Thingsboard diff --git a/rest-client/pom.xml b/rest-client/pom.xml index f868acabf5..c8232512f7 100644 --- a/rest-client/pom.xml +++ b/rest-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT thingsboard rest-client diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml index fbefcd1de9..bd14daf22e 100644 --- a/rule-engine/pom.xml +++ b/rule-engine/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT thingsboard rule-engine diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml index 3ee6496452..886a2169f3 100644 --- a/rule-engine/rule-engine-api/pom.xml +++ b/rule-engine/rule-engine-api/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index 33bb15af00..750701894f 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/tools/pom.xml b/tools/pom.xml index dce8e1606d..4f7c3f48a6 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT thingsboard tools diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index eef23eae9b..452ef6ec9a 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/http/pom.xml b/transport/http/pom.xml index d841c56da7..2408340792 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/lwm2m/pom.xml b/transport/lwm2m/pom.xml index cec30fcbc5..3ba43f5b53 100644 --- a/transport/lwm2m/pom.xml +++ b/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index edf3727320..e31d618890 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/pom.xml b/transport/pom.xml index d744f7eb2f..dd1c415e6c 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT thingsboard transport diff --git a/transport/snmp/pom.xml b/transport/snmp/pom.xml index dcd08c10af..08f23d2c2d 100644 --- a/transport/snmp/pom.xml +++ b/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT transport diff --git a/ui-ngx/pom.xml b/ui-ngx/pom.xml index 1384b1c3e6..9515f3b3ec 100644 --- a/ui-ngx/pom.xml +++ b/ui-ngx/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT thingsboard org.thingsboard From 2c131f67677e7296eed26131a1c229a12e147930 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 12 Dec 2024 18:02:51 +0200 Subject: [PATCH 005/108] Introduce dynamic form properties settings, introduce dynamic form component. Migrate from json schema form to dynamic form. --- .../add-widget-dialog.component.ts | 3 + .../dashboard-page/edit-widget.component.ts | 3 + .../config/widget-settings.component.html | 6 +- .../config/widget-settings.component.ts | 32 +- .../widget/lib/scada/scada-symbol.models.ts | 143 +- .../dynamic-form-properties.component.html} | 26 +- .../dynamic-form-properties.component.scss} | 8 +- .../dynamic-form-properties.component.ts} | 67 +- ...ynamic-form-property-panel.component.html} | 173 ++- ...ynamic-form-property-panel.component.scss} | 10 +- .../dynamic-form-property-panel.component.ts | 212 +++ .../dynamic-form-property-row.component.html} | 10 +- .../dynamic-form-property-row.component.scss} | 2 +- .../dynamic-form-property-row.component.ts} | 95 +- ...ynamic-form-select-item-row.component.html | 37 + ...ynamic-form-select-item-row.component.scss | 23 + .../dynamic-form-select-item-row.component.ts | 172 +++ .../dynamic-form-select-items.component.html | 61 + .../dynamic-form-select-items.component.scss | 43 + .../dynamic-form-select-items.component.ts | 204 +++ .../dynamic-form/dynamic-form.component.html | 117 ++ .../dynamic-form/dynamic-form.component.scss | 22 + .../dynamic-form/dynamic-form.component.ts | 265 ++++ ...cada-symbol-object-settings.component.html | 57 +- .../scada-symbol-object-settings.component.ts | 85 +- .../scada-symbol-object-settings.models.ts | 32 +- .../common/widget-settings-common.module.ts | 32 +- .../widget/widget-config.component.html | 15 + .../widget/widget-config.component.ts | 48 +- .../home/models/widget-component.models.ts | 2 + ...scada-symbol-metadata-components.module.ts | 14 +- .../scada-symbol-metadata.component.html | 4 +- .../scada-symbol-property-panel.component.ts | 136 -- .../scada-symbol-editor.models.ts | 43 +- .../pages/widget/widget-editor.component.html | 9 + .../pages/widget/widget-editor.component.ts | 28 +- .../home/pages/widget/widget-editor.models.ts | 145 +- .../pages/widget/widget-library.module.ts | 2 + .../components/value-input.component.html | 15 +- .../components/value-input.component.ts | 11 + .../models/ace/widget-completion.models.ts | 1184 +++++++++-------- .../app/shared/models/dynamic-form.models.ts | 385 ++++++ ui-ngx/src/app/shared/models/widget.models.ts | 8 + .../assets/locale/locale.constant-en_US.json | 80 +- 44 files changed, 2772 insertions(+), 1297 deletions(-) rename ui-ngx/src/app/modules/home/{pages/scada-symbol/metadata-components/scada-symbol-properties.component.html => components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.html} (70%) rename ui-ngx/src/app/modules/home/{pages/scada-symbol/metadata-components/scada-symbol-properties.component.scss => components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.scss} (90%) rename ui-ngx/src/app/modules/home/{pages/scada-symbol/metadata-components/scada-symbol-properties.component.ts => components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.ts} (75%) rename ui-ngx/src/app/modules/home/{pages/scada-symbol/metadata-components/scada-symbol-property-panel.component.html => components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.html} (54%) rename ui-ngx/src/app/modules/home/{pages/scada-symbol/metadata-components/scada-symbol-property-panel.component.scss => components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.scss} (82%) create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.ts rename ui-ngx/src/app/modules/home/{pages/scada-symbol/metadata-components/scada-symbol-property-row.component.html => components/widget/lib/settings/common/dynamic-form/dynamic-form-property-row.component.html} (84%) rename ui-ngx/src/app/modules/home/{pages/scada-symbol/metadata-components/scada-symbol-property-row.component.scss => components/widget/lib/settings/common/dynamic-form/dynamic-form-property-row.component.scss} (94%) rename ui-ngx/src/app/modules/home/{pages/scada-symbol/metadata-components/scada-symbol-property-row.component.ts => components/widget/lib/settings/common/dynamic-form/dynamic-form-property-row.component.ts} (69%) create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-item-row.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-item-row.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-item-row.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-items.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-items.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-items.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.ts delete mode 100644 ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-property-panel.component.ts create mode 100644 ui-ngx/src/app/shared/models/dynamic-form.models.ts diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.ts index 709159aec3..4617f7a4ed 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.ts @@ -30,6 +30,7 @@ import { isDefined, isDefinedAndNotNull, isString } from '@core/utils'; import { TranslateService } from '@ngx-translate/core'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models'; +import { jsonFormSchemaToFormProperties } from '@shared/models/dynamic-form.models'; export interface AddWidgetDialogData { dashboard: Dashboard; @@ -108,6 +109,7 @@ export class AddWidgetDialogComponent extends DialogComponent
{{definedDirectiveError}}
- + + diff --git a/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts index d8060d7d0e..6e1131cc3e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts @@ -40,13 +40,14 @@ import { Subscription } from 'rxjs'; import { TranslateService } from '@ngx-translate/core'; import { JsonFormComponent } from '@shared/components/json-form/json-form.component'; import { JsonFormComponentData } from '@shared/components/json-form/json-form-component.models'; -import { IWidgetSettingsComponent, Widget, WidgetSettings } from '@shared/models/widget.models'; +import { DynamicFormData, IWidgetSettingsComponent, Widget, WidgetSettings } from '@shared/models/widget.models'; import { widgetSettingsComponentsMap } from '@home/components/widget/lib/settings/widget-settings.module'; import { Dashboard } from '@shared/models/dashboard.models'; import { WidgetService } from '@core/http/widget.service'; import { IAliasController } from '@core/api/widget-api.models'; import { WidgetConfigComponentData } from '@home/models/widget-component.models'; import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-config.component.models'; +import { FormProperty } from '@shared/models/dynamic-form.models'; @Component({ selector: 'tb-widget-settings', @@ -67,8 +68,6 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnDestroy, @ViewChild('definedSettingsContent', {read: ViewContainerRef, static: true}) definedSettingsContainer: ViewContainerRef; - @ViewChild('jsonFormComponent') jsonFormComponent: JsonFormComponent; - @Input() disabled: boolean; @@ -91,6 +90,8 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnDestroy, definedDirectiveError: string; + settingsForm?: FormProperty[]; + widgetSettingsFormGroup: UntypedFormGroup; changeSubscription: Subscription; @@ -98,7 +99,7 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnDestroy, private definedSettingsComponentRef: ComponentRef; private definedSettingsComponent: IWidgetSettingsComponent; - private widgetSettingsFormData: JsonFormComponentData; + private widgetSettingsFormData: JsonFormComponentData | DynamicFormData; private propagateChange = (_v: any) => { }; constructor(private translate: TranslateService, @@ -165,8 +166,13 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnDestroy, } } - writeValue(value: JsonFormComponentData): void { + writeValue(value: JsonFormComponentData | DynamicFormData): void { this.widgetSettingsFormData = value; + if ('settingsForm' in this.widgetSettingsFormData) { + this.settingsForm = this.widgetSettingsFormData.settingsForm; + } else { + this.settingsForm = null; + } if (this.changeSubscription) { this.changeSubscription.unsubscribe(); this.changeSubscription = null; @@ -181,10 +187,12 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnDestroy, this.updateModel(settings); }); } else { - this.widgetSettingsFormGroup.get('settings').patchValue(this.widgetSettingsFormData, {emitEvent: false}); + const settingsValue = this.useJsonForm() ? this.widgetSettingsFormData : this.widgetSettingsFormData.model; + this.widgetSettingsFormGroup.get('settings').patchValue(settingsValue, {emitEvent: false}); this.changeSubscription = this.widgetSettingsFormGroup.get('settings').valueChanges.subscribe( - (widgetSettingsFormData: JsonFormComponentData) => { - this.updateModel(widgetSettingsFormData.model); + (data: JsonFormComponentData | WidgetSettings) => { + const settings = this.useJsonForm() ? data.model : data; + this.updateModel(settings); } ); } @@ -196,7 +204,11 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnDestroy, } useJsonForm(): boolean { - return !this.settingsDirective || !this.settingsDirective.length; + return (!this.settingsDirective || !this.settingsDirective.length) && !this.settingsForm?.length; + } + + useDynamicForm(): boolean { + return (!this.settingsDirective || !this.settingsDirective.length) && !!this.settingsForm?.length; } private updateModel(settings: WidgetSettings) { @@ -246,7 +258,7 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnDestroy, } }; } - } else if (this.useJsonForm()) { + } else if (this.useJsonForm() || this.useDynamicForm()) { if (!this.widgetSettingsFormGroup.get('settings').valid) { return { widgetSettings: { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts index 8a782a14a2..d646990351 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts @@ -65,6 +65,7 @@ import { catchError, map, take, takeUntil } from 'rxjs/operators'; import { isSvgIcon, splitIconName } from '@shared/models/icon.models'; import { MatIconRegistry } from '@angular/material/icon'; import { RafService } from '@core/services/raf.service'; +import { defaultFormPropertyValue, FormProperty, FormPropertyType } from '@shared/models/dynamic-form.models'; export interface ScadaSymbolApi { generateElementId: () => string; @@ -151,60 +152,6 @@ export interface ScadaSymbolBehaviorAction extends ScadaSymbolBehaviorBase { export type ScadaSymbolBehavior = ScadaSymbolBehaviorValue & ScadaSymbolBehaviorAction; -export enum ScadaSymbolPropertyType { - text = 'text', - number = 'number', - switch = 'switch', - color = 'color', - color_settings = 'color_settings', - font = 'font', - units = 'units', - icon = 'icon' -} - -export const scadaSymbolPropertyTypes = Object.keys(ScadaSymbolPropertyType) as ScadaSymbolPropertyType[]; - -export const scadaSymbolPropertyTypeTranslations = new Map( - [ - [ScadaSymbolPropertyType.text, 'scada.property.type-text'], - [ScadaSymbolPropertyType.number, 'scada.property.type-number'], - [ScadaSymbolPropertyType.switch, 'scada.property.type-switch'], - [ScadaSymbolPropertyType.color, 'scada.property.type-color'], - [ScadaSymbolPropertyType.color_settings, 'scada.property.type-color-settings'], - [ScadaSymbolPropertyType.font, 'scada.property.type-font'], - [ScadaSymbolPropertyType.units, 'scada.property.type-units'], - [ScadaSymbolPropertyType.icon, 'scada.property.type-icon'] - ] -); - -export const scadaSymbolPropertyRowClasses = - ['column', 'column-xs', 'column-lt-md', 'align-start', 'no-border', 'no-gap', 'no-padding', 'same-padding']; - -export const scadaSymbolPropertyFieldClasses = - ['medium-width', 'flex', 'flex-xs', 'flex-lt-md']; - -export interface ScadaSymbolPropertyBase { - id: string; - name: string; - type: ScadaSymbolPropertyType; - default: any; - required?: boolean; - subLabel?: string; - divider?: boolean; - fieldSuffix?: string; - disableOnProperty?: string; - rowClass?: string; - fieldClass?: string; -} - -export interface ScadaSymbolNumberProperty extends ScadaSymbolPropertyBase { - min?: number; - max?: number; - step?: number; -} - -export type ScadaSymbolProperty = ScadaSymbolPropertyBase & ScadaSymbolNumberProperty; - export interface ScadaSymbolMetadata { title: string; description?: string; @@ -215,7 +162,7 @@ export interface ScadaSymbolMetadata { stateRender?: ScadaSymbolStateRenderFunction; tags: ScadaSymbolTag[]; behavior: ScadaSymbolBehavior[]; - properties: ScadaSymbolProperty[]; + properties: FormProperty[]; } export const emptyMetadata = (width?: number, height?: number): ScadaSymbolMetadata => ({ @@ -507,7 +454,7 @@ export const defaultScadaSymbolObjectSettings = (metadata: ScadaSymbolMetadata): } } for (const property of metadata.properties) { - settings.properties[property.id] = property.default; + settings.properties[property.id] = defaultFormPropertyValue(property); } return settings; }; @@ -1020,33 +967,73 @@ export class ScadaSymbolObject { return Array.isArray(element) ? element : [element]; } - private getProperty(id: string): ScadaSymbolProperty { - return this.metadata.properties.find(p => p.id === id); + private getProperty(...ids: string[]): FormProperty { + let found: FormProperty; + let properties = this.metadata.properties; + for (const id of ids) { + if (properties) { + found = properties.find(p => p.id === id); + if (found && found.type === FormPropertyType.fieldset) { + properties = found.properties; + } else { + properties = null; + } + } else { + found = null; + } + } + return found; + } + + private getSettingsValue(...ids: string[]): any { + let found: any; + let properties = this.settings.properties; + for (const id of ids) { + if (properties) { + found = properties[id]; + if (found && typeof found === 'object') { + properties = found; + } else { + properties = null; + } + } else { + found = null; + } + } + return found; } - private getPropertyValue(id: string): any { - const property = this.getProperty(id); + private getPropertyValue(...ids: string[]): any { + const property = this.getProperty(...ids); if (property) { - const value = this.settings.properties[id]; - if (isDefinedAndNotNull(value)) { - if (property.type === ScadaSymbolPropertyType.color_settings) { - return ColorProcessor.fromSettings(value); - } else if (property.type === ScadaSymbolPropertyType.text) { - const result = this.ctx.utilsService.customTranslation(value, value); - const entityInfo = this.ctx.defaultSubscription.getFirstEntityInfo(); - return createLabelFromSubscriptionEntityInfo(entityInfo, result); + if (property.type === FormPropertyType.fieldset) { + const propertyValue: {[id: string]: any} = {}; + for (const childProperty of property.properties) { + propertyValue[childProperty.id] = this.getPropertyValue(...ids, childProperty.id); } - return value; + return propertyValue; } else { - switch (property.type) { - case ScadaSymbolPropertyType.text: - return ''; - case ScadaSymbolPropertyType.number: - return 0; - case ScadaSymbolPropertyType.color: - return '#000'; - case ScadaSymbolPropertyType.color_settings: - return ColorProcessor.fromSettings(constantColor('#000')); + const value = this.getSettingsValue(...ids); + if (isDefinedAndNotNull(value)) { + if (property.type === FormPropertyType.color_settings) { + return ColorProcessor.fromSettings(value); + } else if (property.type === FormPropertyType.text) { + const result = this.ctx.utilsService.customTranslation(value, value); + const entityInfo = this.ctx.defaultSubscription.getFirstEntityInfo(); + return createLabelFromSubscriptionEntityInfo(entityInfo, result); + } + return value; + } else { + switch (property.type) { + case FormPropertyType.text: + return ''; + case FormPropertyType.number: + return 0; + case FormPropertyType.color: + return '#000'; + case FormPropertyType.color_settings: + return ColorProcessor.fromSettings(constantColor('#000')); + } } } } else { diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-properties.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.html similarity index 70% rename from ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-properties.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.html index 684609937e..72d008b84e 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-properties.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.html @@ -15,12 +15,12 @@ limitations under the License. --> -
-
+
+
-
scada.property.id
-
scada.property.name
-
scada.property.type
+
dynamic-form.property.id
+
dynamic-form.property.name
+
dynamic-form.property.type
- - + +
- {{ 'scada.property.no-properties' | translate }} + {{ 'dynamic-form.property.no-properties' | translate }} diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-properties.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.scss similarity index 90% rename from ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-properties.component.scss rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.scss index 094ea0668b..d3bc52b22e 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-properties.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.scss @@ -13,9 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -.tb-scada-symbol-properties { +.tb-dynamic-form-properties { flex: 1; - margin: 12px; + &:not(.no-margin) { + margin: 12px; + } .tb-form-table-header-cell { &.tb-id-header { flex: 1 1 40%; @@ -40,7 +42,7 @@ } .tb-form-table-body { overflow: auto; - tb-scada-symbol-metadata-property-row { + tb-dynamic-form-property-row { overflow: hidden; } } diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-properties.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.ts similarity index 75% rename from ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-properties.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.ts index 840302161b..e3c0cd1251 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-properties.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.ts @@ -15,7 +15,7 @@ /// import { - Component, + Component, DestroyRef, forwardRef, HostBinding, Input, @@ -35,43 +35,61 @@ import { UntypedFormGroup, Validator } from '@angular/forms'; -import { ScadaSymbolProperty, ScadaSymbolPropertyType } from '@home/components/widget/lib/scada/scada-symbol.models'; import { CdkDragDrop } from '@angular/cdk/drag-drop'; -import { - propertyValid, - ScadaSymbolPropertyRowComponent -} from '@home/pages/scada-symbol/metadata-components/scada-symbol-property-row.component'; import { TranslateService } from '@ngx-translate/core'; +import { FormProperty, FormPropertyType } from '@shared/models/dynamic-form.models'; +import { + DynamicFormPropertyRowComponent, + propertyValid +} from '@home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-row.component'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ - selector: 'tb-scada-symbol-metadata-properties', - templateUrl: './scada-symbol-properties.component.html', - styleUrls: ['./scada-symbol-properties.component.scss'], + selector: 'tb-dynamic-form-properties', + templateUrl: './dynamic-form-properties.component.html', + styleUrls: ['./dynamic-form-properties.component.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => ScadaSymbolPropertiesComponent), + useExisting: forwardRef(() => DynamicFormPropertiesComponent), multi: true }, { provide: NG_VALIDATORS, - useExisting: forwardRef(() => ScadaSymbolPropertiesComponent), + useExisting: forwardRef(() => DynamicFormPropertiesComponent), multi: true } ], encapsulation: ViewEncapsulation.None }) -export class ScadaSymbolPropertiesComponent implements ControlValueAccessor, OnInit, Validator { +export class DynamicFormPropertiesComponent implements ControlValueAccessor, OnInit, Validator { @HostBinding('style.display') styleDisplay = 'flex'; @HostBinding('style.overflow') styleOverflow = 'hidden'; + @HostBinding('style.height') + get containerHeight(): string { + return this.fillHeight ? '100%': 'auto'; + } - @ViewChildren(ScadaSymbolPropertyRowComponent) - propertyRows: QueryList; + @ViewChildren(DynamicFormPropertyRowComponent) + propertyRows: QueryList; @Input() disabled: boolean; + @Input() + @coerceBoolean() + noBorder = false; + + @Input() + @coerceBoolean() + noMargin = false; + + @Input() + @coerceBoolean() + fillHeight = false; + booleanPropertyIds: string[] = []; propertiesFormGroup: UntypedFormGroup; @@ -85,6 +103,7 @@ export class ScadaSymbolPropertiesComponent implements ControlValueAccessor, OnI private propagateChange = (_val: any) => {}; constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef, private translate: TranslateService) { } @@ -92,13 +111,15 @@ export class ScadaSymbolPropertiesComponent implements ControlValueAccessor, OnI this.propertiesFormGroup = this.fb.group({ properties: this.fb.array([]) }); - this.propertiesFormGroup.valueChanges.subscribe( + this.propertiesFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => { - let properties: ScadaSymbolProperty[] = this.propertiesFormGroup.get('properties').value; + let properties: FormProperty[] = this.propertiesFormGroup.get('properties').value; if (properties) { properties = properties.filter(p => propertyValid(p)); } - this.booleanPropertyIds = properties.filter(p => p.type === ScadaSymbolPropertyType.switch).map(p => p.id); + this.booleanPropertyIds = properties.filter(p => p.type === FormPropertyType.switch).map(p => p.id); properties.forEach((p, i) => { if (p.disableOnProperty && !this.booleanPropertyIds.includes(p.disableOnProperty)) { p.disableOnProperty = null; @@ -127,10 +148,10 @@ export class ScadaSymbolPropertiesComponent implements ControlValueAccessor, OnI } } - writeValue(value: ScadaSymbolProperty[] | undefined): void { + writeValue(value: FormProperty[] | undefined): void { const properties= value || []; this.propertiesFormGroup.setControl('properties', this.preparePropertiesFormArray(properties), {emitEvent: false}); - this.booleanPropertyIds = properties.filter(p => p.type === ScadaSymbolPropertyType.switch).map(p => p.id); + this.booleanPropertyIds = properties.filter(p => p.type === FormPropertyType.switch).map(p => p.id); } public validate(c: UntypedFormControl) { @@ -141,7 +162,7 @@ export class ScadaSymbolPropertiesComponent implements ControlValueAccessor, OnI for (const control of notUniqueControls) { control.updateValueAndValidity({onlySelf: false, emitEvent: false}); if (control.hasError('propertyIdNotUnique')) { - this.errorText = this.translate.instant('scada.property.not-unique-property-ids-error'); + this.errorText = this.translate.instant('dynamic-form.property.not-unique-property-ids-error'); } } const valid = this.propertiesFormGroup.valid; @@ -185,10 +206,10 @@ export class ScadaSymbolPropertiesComponent implements ControlValueAccessor, OnI } addProperty() { - const property: ScadaSymbolProperty = { + const property: FormProperty = { id: '', name: '', - type: ScadaSymbolPropertyType.text, + type: FormPropertyType.text, default: '' }; const propertiesArray = this.propertiesFormGroup.get('properties') as UntypedFormArray; @@ -202,7 +223,7 @@ export class ScadaSymbolPropertiesComponent implements ControlValueAccessor, OnI }); } - private preparePropertiesFormArray(properties: ScadaSymbolProperty[] | undefined): UntypedFormArray { + private preparePropertiesFormArray(properties: FormProperty[] | undefined): UntypedFormArray { const propertiesControls: Array = []; if (properties) { properties.forEach((property) => { diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-property-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.html similarity index 54% rename from ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-property-panel.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.html index 94efffca85..5b28248bf2 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-property-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.html @@ -15,48 +15,110 @@ limitations under the License. --> -
-
{{ panelTitle | translate }}
-
+
+
{{ panelTitle | translate }}
+
-
scada.property.id
+
dynamic-form.property.id
-
scada.property.name
+
dynamic-form.property.name
-
scada.property.type
+
dynamic-form.property.group-title
+ + + +
+
+
dynamic-form.property.type
- - {{ scadaSymbolPropertyTypeTranslations.get(type) | translate }} + + {{ formPropertyTypeTranslations.get(type) | translate }}
-
-
scada.property.default-value
- +
dynamic-form.property.number-settings
+
+
dynamic-form.property.min
+ + + + +
dynamic-form.property.max
+ + + + +
dynamic-form.property.step
+ + + +
+
+
+ + + + {{ 'dynamic-form.property.properties' | translate }} + + + + + + + +
+ +
+ + + + {{ 'dynamic-form.property.select-options' | translate }} + + + +
+ + {{ 'dynamic-form.property.enable-multiple-select' | translate }} + +
+ + +
+
+
+
+
+
dynamic-form.property.default-value
+ - - - @@ -66,78 +128,69 @@ [step]="propertyFormGroup.get('step').value" type="number" placeholder="{{ 'widget-config.set' | translate }}"> - - - - + + + + {{ item.label | customTranslate }} + + + + + {{ item.label | customTranslate }} + + +
-
-
scada.property.number-settings
-
-
scada.property.min
- - - - -
scada.property.max
- - - - -
scada.property.step
- - - -
-
-
+
- {{ 'scada.property.value-required' | translate }} + {{ 'dynamic-form.property.value-required' | translate }}
- {{ 'scada.property.advanced-ui-settings' | translate }} + {{ 'dynamic-form.property.advanced-ui-settings' | translate }} -
-
scada.property.sub-label
+
+
dynamic-form.property.sub-label
-
+
- {{ 'scada.property.vertical-divider-after' | translate }} + {{ 'dynamic-form.property.vertical-divider-after' | translate }}
-
-
scada.property.input-field-suffix
+
dynamic-form.property.input-field-suffix
-
scada.property.disable-on-property
+
dynamic-form.property.disable-on-property
@@ -148,20 +201,28 @@
-
scada.property.property-row-classes
+ + +
+
+
dynamic-form.property.property-row-classes
- + {{ clazz }}
-
-
scada.property.property-field-classes
+
+
dynamic-form.property.property-field-classes
- + {{ clazz }} @@ -171,7 +232,7 @@
-
+
@@ -42,7 +42,7 @@ type="button" mat-icon-button (click)="propertyRemoved.emit()" - matTooltip="{{ 'scada.property.remove-property' | translate }}" + matTooltip="{{ 'dynamic-form.property.remove-property' | translate }}" matTooltipPosition="above"> delete diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-property-row.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-row.component.scss similarity index 94% rename from ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-property-row.component.scss rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-row.component.scss index 5c0b4f0166..7ba115b7fe 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-property-row.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-row.component.scss @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -.tb-scada-symbol-metadata-property-row { +.tb-dynamic-form-property-row { .tb-id-field { flex: 1 1 40%; } diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-property-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-row.component.ts similarity index 69% rename from ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-property-row.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-row.component.ts index 3da851063f..c734c91fab 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-property-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-row.component.ts @@ -16,7 +16,7 @@ import { ChangeDetectorRef, - Component, + Component, DestroyRef, ElementRef, EventEmitter, forwardRef, @@ -39,38 +39,39 @@ import { ValidatorFn, Validators } from '@angular/forms'; -import { - ScadaSymbolProperty, - ScadaSymbolPropertyType, - scadaSymbolPropertyTypes, - scadaSymbolPropertyTypeTranslations -} from '@home/components/widget/lib/scada/scada-symbol.models'; import { deepClone } from '@core/utils'; import { MatButton } from '@angular/material/button'; import { TbPopoverService } from '@shared/components/popover.service'; import { constantColor, Font } from '@shared/models/widget-settings.models'; import { - ScadaSymbolPropertyPanelComponent -} from '@home/pages/scada-symbol/metadata-components/scada-symbol-property-panel.component'; + FormProperty, + FormPropertyType, + formPropertyTypes, + formPropertyTypeTranslations +} from '@shared/models/dynamic-form.models'; +import { + DynamicFormPropertiesComponent +} from '@home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component'; import { - ScadaSymbolPropertiesComponent -} from '@home/pages/scada-symbol/metadata-components/scada-symbol-properties.component'; + DynamicFormPropertyPanelComponent +} from '@home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -export const propertyValid = (property: ScadaSymbolProperty): boolean => !(!property.id || !property.name || !property.type); +export const propertyValid = (property: FormProperty): boolean => !(!property.id || !property.name || !property.type); -export const defaultPropertyValue = (type: ScadaSymbolPropertyType): any => { +export const defaultPropertyValue = (type: FormPropertyType): any => { switch (type) { - case ScadaSymbolPropertyType.text: + case FormPropertyType.text: return ''; - case ScadaSymbolPropertyType.number: + case FormPropertyType.number: return 0; - case ScadaSymbolPropertyType.switch: + case FormPropertyType.switch: return false; - case ScadaSymbolPropertyType.color: + case FormPropertyType.color: return '#000'; - case ScadaSymbolPropertyType.color_settings: + case FormPropertyType.color_settings: return constantColor('#000'); - case ScadaSymbolPropertyType.font: + case FormPropertyType.font: return { size: 12, sizeUnit: 'px', @@ -79,32 +80,35 @@ export const defaultPropertyValue = (type: ScadaSymbolPropertyType): any => { style: 'normal', lineHeight: '1' } as Font; - case ScadaSymbolPropertyType.units: + case FormPropertyType.units: return ''; - case ScadaSymbolPropertyType.icon: + case FormPropertyType.icon: return 'star'; + case FormPropertyType.fieldset: + case FormPropertyType.select: + return null; } }; @Component({ - selector: 'tb-scada-symbol-metadata-property-row', - templateUrl: './scada-symbol-property-row.component.html', - styleUrls: ['./scada-symbol-property-row.component.scss'], + selector: 'tb-dynamic-form-property-row', + templateUrl: './dynamic-form-property-row.component.html', + styleUrls: ['./dynamic-form-property-row.component.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => ScadaSymbolPropertyRowComponent), + useExisting: forwardRef(() => DynamicFormPropertyRowComponent), multi: true }, { provide: NG_VALIDATORS, - useExisting: forwardRef(() => ScadaSymbolPropertyRowComponent), + useExisting: forwardRef(() => DynamicFormPropertyRowComponent), multi: true } ], encapsulation: ViewEncapsulation.None }) -export class ScadaSymbolPropertyRowComponent implements ControlValueAccessor, OnInit, Validator { +export class DynamicFormPropertyRowComponent implements ControlValueAccessor, OnInit, Validator { @ViewChild('idInput') idInput: ElementRef; @@ -112,8 +116,8 @@ export class ScadaSymbolPropertyRowComponent implements ControlValueAccessor, On @ViewChild('editButton') editButton: MatButton; - scadaSymbolPropertyTypes = scadaSymbolPropertyTypes; - scadaSymbolPropertyTypeTranslations = scadaSymbolPropertyTypeTranslations; + formPropertyTypes = formPropertyTypes; + formPropertyTypeTranslations = formPropertyTypeTranslations; @Input() disabled: boolean; @@ -129,16 +133,17 @@ export class ScadaSymbolPropertyRowComponent implements ControlValueAccessor, On propertyRowFormGroup: UntypedFormGroup; - modelValue: ScadaSymbolProperty; + modelValue: FormProperty; private propagateChange = (_val: any) => {}; constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef, private cd: ChangeDetectorRef, private popoverService: TbPopoverService, private renderer: Renderer2, private viewContainerRef: ViewContainerRef, - private propertiesComponent: ScadaSymbolPropertiesComponent) { + private propertiesComponent: DynamicFormPropertiesComponent) { } ngOnInit() { @@ -147,10 +152,14 @@ export class ScadaSymbolPropertyRowComponent implements ControlValueAccessor, On name: [null, [Validators.required]], type: [null, [Validators.required]] }); - this.propertyRowFormGroup.valueChanges.subscribe( + this.propertyRowFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( () => this.updateModel() ); - this.propertyRowFormGroup.get('type').valueChanges.subscribe((newType: ScadaSymbolPropertyType) => { + this.propertyRowFormGroup.get('type').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((newType: FormPropertyType) => { this.onTypeChanged(newType); }); } @@ -171,7 +180,7 @@ export class ScadaSymbolPropertyRowComponent implements ControlValueAccessor, On } } - writeValue(value: ScadaSymbolProperty): void { + writeValue(value: FormProperty): void { this.modelValue = value; this.propertyRowFormGroup.patchValue( { @@ -197,14 +206,14 @@ export class ScadaSymbolPropertyRowComponent implements ControlValueAccessor, On booleanPropertyIds: this.booleanPropertyIds, property: deepClone(this.modelValue) }; - const scadaSymbolPropertyPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, - this.viewContainerRef, ScadaSymbolPropertyPanelComponent, ['leftOnly', 'leftTopOnly', 'leftBottomOnly'], true, null, + const dynamicFormPropertyPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, DynamicFormPropertyPanelComponent, ['leftOnly', 'leftTopOnly', 'leftBottomOnly'], true, null, ctx, {}, {}, {}, true); - scadaSymbolPropertyPanelPopover.tbComponentRef.instance.popover = scadaSymbolPropertyPanelPopover; - scadaSymbolPropertyPanelPopover.tbComponentRef.instance.propertySettingsApplied.subscribe((property) => { - scadaSymbolPropertyPanelPopover.hide(); + dynamicFormPropertyPanelPopover.tbComponentRef.instance.popover = dynamicFormPropertyPanelPopover; + dynamicFormPropertyPanelPopover.tbComponentRef.instance.propertySettingsApplied.subscribe((property) => { + dynamicFormPropertyPanelPopover.hide(); this.propertyRowFormGroup.patchValue( { id: property.id, @@ -215,7 +224,7 @@ export class ScadaSymbolPropertyRowComponent implements ControlValueAccessor, On this.modelValue = property; this.propagateChange(this.modelValue); }); - scadaSymbolPropertyPanelPopover.tbDestroy.subscribe(() => { + dynamicFormPropertyPanelPopover.tbDestroy.subscribe(() => { if (!propertyValid(this.modelValue)) { editCanceled(); } @@ -244,7 +253,7 @@ export class ScadaSymbolPropertyRowComponent implements ControlValueAccessor, On propertyIdNotUnique: true }; } - const property: ScadaSymbolProperty = {...this.modelValue, ...this.propertyRowFormGroup.value}; + const property: FormProperty = {...this.modelValue, ...this.propertyRowFormGroup.value}; if (!propertyValid(property)) { return { property: true @@ -269,13 +278,13 @@ export class ScadaSymbolPropertyRowComponent implements ControlValueAccessor, On }; } - private onTypeChanged(newType: ScadaSymbolPropertyType) { + private onTypeChanged(newType: FormPropertyType) { this.modelValue = {...this.modelValue, ...{type: newType}}; this.modelValue.default = defaultPropertyValue(newType); } private updateModel() { - const value: ScadaSymbolProperty = this.propertyRowFormGroup.value; + const value: FormProperty = this.propertyRowFormGroup.value; this.modelValue = {...this.modelValue, ...value}; this.propagateChange(this.modelValue); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-item-row.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-item-row.component.html new file mode 100644 index 0000000000..5691ba532b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-item-row.component.html @@ -0,0 +1,37 @@ + +
+ + + + + +
+ +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-item-row.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-item-row.component.scss new file mode 100644 index 0000000000..7d9322262a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-item-row.component.scss @@ -0,0 +1,23 @@ +/** + * 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. + */ +.tb-dynamic-form-select-item-row { + .tb-value-field { + flex: 1 1 70%; + } + .tb-label-field { + flex: 1 1 30%; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-item-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-item-row.component.ts new file mode 100644 index 0000000000..3e43c7a3cc --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-item-row.component.ts @@ -0,0 +1,172 @@ +/// +/// 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 { + ChangeDetectorRef, + Component, DestroyRef, + EventEmitter, + forwardRef, + Input, + OnInit, + Output, + ViewEncapsulation +} from '@angular/core'; +import { + ControlValueAccessor, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, + Validator, + ValidatorFn, + Validators +} from '@angular/forms'; +import { isDefinedAndNotNull } from '@core/utils'; +import { FormSelectItem } from '@shared/models/dynamic-form.models'; +import { + DynamicFormSelectItemsComponent +} from '@home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-items.component'; +import { TimeSeriesChartStateSourceType } from '@home/components/widget/lib/chart/time-series-chart.models'; +import { ValueType } from '@shared/models/constants'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; + +export const selectItemValid = (item: FormSelectItem): boolean => isDefinedAndNotNull(item.value) && !!item.label; + +@Component({ + selector: 'tb-dynamic-form-select-item-row', + templateUrl: './dynamic-form-select-item-row.component.html', + styleUrls: ['./dynamic-form-select-item-row.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => DynamicFormSelectItemRowComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => DynamicFormSelectItemRowComponent), + multi: true + } + ], + encapsulation: ViewEncapsulation.None +}) +export class DynamicFormSelectItemRowComponent implements ControlValueAccessor, OnInit, Validator { + + ValueType = ValueType; + + @Input() + disabled: boolean; + + @Input() + index: number; + + @Output() + selectItemRemoved = new EventEmitter(); + + selectItemRowFormGroup: UntypedFormGroup; + + modelValue: FormSelectItem; + + private propagateChange = (_val: any) => {}; + + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef, + private cd: ChangeDetectorRef, + private selectItemsComponent: DynamicFormSelectItemsComponent) { + } + + ngOnInit() { + this.selectItemRowFormGroup = this.fb.group({ + value: [null, [this.selectItemValueValidator()]], + label: [null, [Validators.required]] + }); + this.selectItemRowFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( + () => this.updateModel() + ); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.selectItemRowFormGroup.disable({emitEvent: false}); + } else { + this.selectItemRowFormGroup.enable({emitEvent: false}); + } + } + + writeValue(value: FormSelectItem): void { + this.modelValue = value; + this.selectItemRowFormGroup.patchValue( + { + value: value?.value, + label: value?.label + }, {emitEvent: false} + ); + this.cd.markForCheck(); + } + + public validate(c: UntypedFormControl) { + const valueControl = this.selectItemRowFormGroup.get('value'); + if (valueControl.hasError('itemValueNotUnique')) { + valueControl.updateValueAndValidity({onlySelf: false, emitEvent: false}); + } + if (valueControl.hasError('itemValueNotUnique')) { + this.selectItemRowFormGroup.get('value').markAsTouched(); + return { + itemValueNotUnique: true + }; + } + const item: FormSelectItem = {...this.modelValue, ...this.selectItemRowFormGroup.value}; + if (!selectItemValid(item)) { + return { + selectItem: true + }; + } + return null; + } + + private selectItemValueValidator(): ValidatorFn { + return control => { + if (!control.value) { + return { + required: true + }; + } + if (!this.selectItemsComponent.selectItemValueUnique(control.value, this.index)) { + return { + itemValueNotUnique: true + }; + } + return null; + }; + } + + private updateModel() { + const value: FormSelectItem = this.selectItemRowFormGroup.value; + this.modelValue = {...this.modelValue, ...value}; + this.propagateChange(this.modelValue); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-items.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-items.component.html new file mode 100644 index 0000000000..03e1fdcbf5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-items.component.html @@ -0,0 +1,61 @@ + +
+
+
+
dynamic-form.property.value
+
dynamic-form.property.label
+
+
+
+
+ + +
+ +
+
+
+ +
+
+ +
+
+ + + {{ 'dynamic-form.property.no-options' | translate }} + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-items.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-items.component.scss new file mode 100644 index 0000000000..450056d2d6 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-items.component.scss @@ -0,0 +1,43 @@ +/** + * 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. + */ +.tb-dynamic-form-select-items { + flex: 1; + .tb-form-table-header-cell { + &.tb-value-header { + flex: 1 1 70%; + } + &.tb-label-header { + flex: 1 1 30%; + } + &.tb-actions-header { + width: 80px; + min-width: 80px; + &.disabled { + width: 0; + min-width: 0; + } + } + } + .tb-form-table { + overflow: hidden; + } + .tb-form-table-body { + overflow: auto; + tb-dynamic-form-select-item-row { + overflow: hidden; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-items.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-items.component.ts new file mode 100644 index 0000000000..8d3c167df0 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-items.component.ts @@ -0,0 +1,204 @@ +/// +/// 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, DestroyRef, + forwardRef, + HostBinding, + Input, + OnInit, + QueryList, + ViewChildren, + ViewEncapsulation +} from '@angular/core'; +import { + AbstractControl, + ControlValueAccessor, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + UntypedFormArray, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, + Validator +} from '@angular/forms'; +import { CdkDragDrop } from '@angular/cdk/drag-drop'; +import { TranslateService } from '@ngx-translate/core'; +import { FormSelectItem } from '@shared/models/dynamic-form.models'; +import { + DynamicFormSelectItemRowComponent, + selectItemValid +} from '@home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-item-row.component'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; + +@Component({ + selector: 'tb-dynamic-form-select-items', + templateUrl: './dynamic-form-select-items.component.html', + styleUrls: ['./dynamic-form-select-items.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => DynamicFormSelectItemsComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => DynamicFormSelectItemsComponent), + multi: true + } + ], + encapsulation: ViewEncapsulation.None +}) +export class DynamicFormSelectItemsComponent implements ControlValueAccessor, OnInit, Validator { + + @HostBinding('style.display') styleDisplay = 'flex'; + @HostBinding('style.overflow') styleOverflow = 'hidden'; + + @ViewChildren(DynamicFormSelectItemRowComponent) + selectItemRows: QueryList; + + @Input() + disabled: boolean; + + selectItemsFormGroup: UntypedFormGroup; + + errorText = ''; + + get dragEnabled(): boolean { + return !this.disabled && this.selectItemsFormArray().controls.length > 1; + } + + private propagateChange = (_val: any) => {}; + + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef, + private translate: TranslateService) { + } + + ngOnInit() { + this.selectItemsFormGroup = this.fb.group({ + selectItems: this.fb.array([]) + }); + this.selectItemsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( + () => { + let items: FormSelectItem[] = this.selectItemsFormGroup.get('selectItems').value; + if (items) { + items = items.filter(i => selectItemValid(i)); + } + this.propagateChange(items); + } + ); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.selectItemsFormGroup.disable({emitEvent: false}); + } else { + this.selectItemsFormGroup.enable({emitEvent: false}); + } + } + + writeValue(value: FormSelectItem[] | undefined): void { + const items= value || []; + this.selectItemsFormGroup.setControl('selectItems', this.prepareSelectItemsFormArray(items), {emitEvent: false}); + } + + public validate(c: UntypedFormControl) { + this.errorText = ''; + const itemsArray = this.selectItemsFormGroup.get('selectItems') as UntypedFormArray; + const notUniqueControls = + itemsArray.controls.filter(control => control.hasError('itemValueNotUnique')); + for (const control of notUniqueControls) { + control.updateValueAndValidity({onlySelf: false, emitEvent: false}); + if (control.hasError('itemValueNotUnique')) { + this.errorText = this.translate.instant('dynamic-form.property.not-unique-select-option-value-error'); + } + } + let valid = this.selectItemsFormGroup.valid; + if (valid) { + const items: FormSelectItem[] = this.selectItemsFormGroup.get('selectItems').value; + valid = !items.some(item => !selectItemValid(item)); + } + + return valid ? null : { + selectItems: { + valid: false, + }, + }; + } + + public selectItemValueUnique(value: any, index: number): boolean { + const itemsArray = this.selectItemsFormGroup.get('selectItems') as UntypedFormArray; + for (let i = 0; i < itemsArray.controls.length; i++) { + if (i !== index) { + const otherControl = itemsArray.controls[i]; + if (value === otherControl.value.value) { + return false; + } + } + } + return true; + } + + selectItemDrop(event: CdkDragDrop) { + const itemsArray = this.selectItemsFormGroup.get('selectItems') as UntypedFormArray; + const item = itemsArray.at(event.previousIndex); + itemsArray.removeAt(event.previousIndex); + itemsArray.insert(event.currentIndex, item); + } + + selectItemsFormArray(): UntypedFormArray { + return this.selectItemsFormGroup.get('selectItems') as UntypedFormArray; + } + + trackBySelectItem(index: number, selectItemControl: AbstractControl): any { + return selectItemControl; + } + + removeSelectItem(index: number, emitEvent = true) { + (this.selectItemsFormGroup.get('selectItems') as UntypedFormArray).removeAt(index, {emitEvent}); + } + + addSelectItem() { + const item: FormSelectItem = { + value: '', + label: '' + }; + const itemsArray = this.selectItemsFormGroup.get('selectItems') as UntypedFormArray; + const itemControl = this.fb.control(item, []); + itemsArray.push(itemControl); + } + + private prepareSelectItemsFormArray(items: FormSelectItem[] | undefined): UntypedFormArray { + const selectItemsControls: Array = []; + if (items) { + items.forEach((item) => { + selectItemsControls.push(this.fb.control(item, [])); + }); + } + return this.fb.array(selectItemsControls); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.html new file mode 100644 index 0000000000..1acdcb6962 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.html @@ -0,0 +1,117 @@ + +
+
{{ title }}
+ + + +
+ + + +
{{ propertyGroup.title | customTranslate }}
+
+
+ + + +
+
+ + + +
+
+
+ + + + + + + + + + + + + +
+ + {{ propertyRow.label | customTranslate }} + +
{{ propertyRow.label | customTranslate }}
+
+ + +
{{ property.subLabel | customTranslate }}
+ + +
{{ property.fieldSuffix | customTranslate }}
+
+ + + + {{ item.label | customTranslate }} + + + + + + + + + +
{{ property.fieldSuffix | customTranslate }}
+
+ + + + + + +
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.scss new file mode 100644 index 0000000000..ed4f7d837c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.scss @@ -0,0 +1,22 @@ +/** + * 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 { + .tb-properties-content { + gap: 16px; + display: flex; + flex-direction: column; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.ts new file mode 100644 index 0000000000..ac4348df16 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.ts @@ -0,0 +1,265 @@ +/// +/// 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 { + ChangeDetectorRef, + Component, + DestroyRef, + forwardRef, + Input, + OnChanges, + OnInit, + SimpleChanges +} from '@angular/core'; +import { + ControlValueAccessor, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, + Validator, + ValidatorFn, + Validators +} from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { isDefinedAndNotNull, mergeDeep } from '@core/utils'; +import { + defaultFormProperties, + FormProperty, + FormPropertyContainerType, + FormPropertyGroup, + FormPropertyType, + PropertyConditionFunction, + toPropertyGroups +} from '@shared/models/dynamic-form.models'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; + +@Component({ + selector: 'tb-dynamic-form', + templateUrl: './dynamic-form.component.html', + styleUrls: ['./dynamic-form.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => DynamicFormComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => DynamicFormComponent), + multi: true + } + ] +}) +export class DynamicFormComponent implements OnInit, OnChanges, ControlValueAccessor, Validator { + + FormPropertyContainerType = FormPropertyContainerType; + + FormPropertyType = FormPropertyType; + + @Input() + disabled: boolean; + + @Input() + properties: FormProperty[]; + + @Input() + title: string; + + @Input() + @coerceBoolean() + stroked = false; + + private modelValue: {[id: string]: any}; + + private propagateChange = null; + + private validatorTriggers: string[]; + + public propertiesFormGroup: UntypedFormGroup; + + propertyGroups: FormPropertyGroup[]; + + constructor(protected store: Store, + private destroyRef: DestroyRef, + private fb: UntypedFormBuilder, + private cd: ChangeDetectorRef) { + } + + ngOnInit(): void { + this.propertiesFormGroup = this.fb.group({ + }); + this.propertiesFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.updateModel(); + }); + this.loadMetadata(); + } + + ngOnChanges(changes: SimpleChanges): void { + for (const propName of Object.keys(changes)) { + const change = changes[propName]; + if (!change.firstChange && change.currentValue !== change.previousValue) { + if (['properties'].includes(propName)) { + this.loadMetadata(); + } + } + } + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.propertiesFormGroup.disable({emitEvent: false}); + } else { + this.propertiesFormGroup.enable({emitEvent: false}); + this.updateControlsState(); + } + } + + writeValue(value: {[id: string]: any}): void { + this.modelValue = value || {}; + this.setupValue(); + } + + validate(_c: UntypedFormControl) { + const valid = this.propertiesFormGroup.valid; + return valid ? null : { + properties: { + valid: false, + }, + }; + } + + private loadMetadata() { + this.validatorTriggers = []; + this.propertyGroups = []; + + for (const control of Object.keys(this.propertiesFormGroup.controls)) { + this.propertiesFormGroup.removeControl(control, {emitEvent: false}); + } + if (this.properties) { + for (let property of this.properties) { + property.disabled = false; + property.visible = true; + if (property.condition) { + try { + property.conditionFunction = new Function('property', 'model', property.condition) as PropertyConditionFunction; + } catch (_e) { + } + } + } + this.propertyGroups = toPropertyGroups(this.properties); + for (const property of this.properties) { + if (property.disableOnProperty) { + if (!this.validatorTriggers.includes(property.disableOnProperty)) { + this.validatorTriggers.push(property.disableOnProperty); + } + } + const validators: ValidatorFn[] = []; + if (property.required) { + validators.push(Validators.required); + } + if (property.type === FormPropertyType.number) { + if (isDefinedAndNotNull(property.min)) { + validators.push(Validators.min(property.min)); + } + if (isDefinedAndNotNull(property.max)) { + validators.push(Validators.max(property.max)); + } + } + this.propertiesFormGroup.addControl(property.id, this.fb.control(null, validators), {emitEvent: false}); + } + } + this.setupValue(); + this.cd.markForCheck(); + } + + private calculateControlsState(updateControls = false) { + for (const trigger of this.validatorTriggers) { + const value: boolean = this.propertiesFormGroup.get(trigger).value; + this.properties.filter(p => p.disableOnProperty === trigger).forEach( + (p) => { + p.disabled = !value; + } + ); + } + if (this.properties) { + for (let property of this.properties) { + if (property.conditionFunction) { + property.visible = property.conditionFunction(property, this.modelValue); + } + } + this.propertyGroups.forEach(g => { + g.containers.forEach(container => { + if (container.type === FormPropertyContainerType.fieldset) { + container.visible = container.property.visible; + } else { + container.visible = container.switch?.visible || container.properties.some(p => p.visible); + } + }); + g.visible = g.containers.some(c => c.visible); + }); + } + if (updateControls) { + this.updateControlsState(); + } + } + + private updateControlsState() { + if (this.properties) { + for (let property of this.properties) { + const control = this.propertiesFormGroup.get(property.id); + if (property.visible && !property.disabled) { + control.enable({emitEvent: false}); + control.updateValueAndValidity({emitEvent: false}); + } else { + control.disable({emitEvent: false}); + } + } + } + } + + private setupValue() { + if (this.properties) { + const defaults = defaultFormProperties(this.properties); + this.modelValue = mergeDeep<{[id: string]: any}>(defaults, this.modelValue); + this.propertiesFormGroup.patchValue( + this.modelValue, {emitEvent: false} + ); + this.calculateControlsState(); + this.setDisabledState(this.disabled); + } + } + + private updateModel() { + this.modelValue = this.propertiesFormGroup.getRawValue(); + this.calculateControlsState(true); + this.propagateChange(this.modelValue); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/scada/scada-symbol-object-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/scada/scada-symbol-object-settings.component.html index c8a0c5cb2a..41c40f8623 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/scada/scada-symbol-object-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/scada/scada-symbol-object-settings.component.html @@ -36,58 +36,11 @@
-
-
widget-config.appearance
-
-
- - {{ propertyRow.label | customTranslate }} - -
{{ propertyRow.label | customTranslate }}
-
- -
{{ property.subLabel | customTranslate }}
- - -
{{ property.fieldSuffix | customTranslate }}
-
- - - - - - -
{{ property.fieldSuffix | customTranslate }}
-
- - - - - - -
-
-
-
+ + + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/scada/scada-symbol-object-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/scada/scada-symbol-object-settings.component.ts index e68788cc92..f6113a9e85 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/scada/scada-symbol-object-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/scada/scada-symbol-object-settings.component.ts @@ -22,9 +22,7 @@ import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, - Validator, - ValidatorFn, - Validators + Validator } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -33,19 +31,16 @@ import { parseScadaSymbolMetadataFromContent, ScadaSymbolBehaviorType, ScadaSymbolMetadata, - ScadaSymbolObjectSettings, - ScadaSymbolPropertyType + ScadaSymbolObjectSettings } from '@home/components/widget/lib/scada/scada-symbol.models'; import { IAliasController } from '@core/api/widget-api.models'; import { TargetDevice, widgetType } from '@shared/models/widget.models'; -import { isDefinedAndNotNull, mergeDeep } from '@core/utils'; +import { mergeDeep } from '@core/utils'; import { ScadaSymbolBehaviorGroup, - ScadaSymbolPropertyRow, - toBehaviorGroups, - toPropertyRows + toBehaviorGroups } from '@home/components/widget/lib/settings/common/scada/scada-symbol-object-settings.models'; -import { merge, Observable, of, Subscription } from 'rxjs'; +import { Observable, of } from 'rxjs'; import { WidgetActionCallbacks } from '@home/components/widget/action/manage-widget-actions.component.models'; import { ImageService } from '@core/http/image.service'; import { map } from 'rxjs/operators'; @@ -71,8 +66,6 @@ export class ScadaSymbolObjectSettingsComponent implements OnInit, OnChanges, Co ScadaSymbolBehaviorType = ScadaSymbolBehaviorType; - ScadaSymbolPropertyType = ScadaSymbolPropertyType; - @Input() disabled: boolean; @@ -101,14 +94,10 @@ export class ScadaSymbolObjectSettingsComponent implements OnInit, OnChanges, Co private propagateChange = null; - private validatorTriggers: string[]; - private validatorSubscription: Subscription; - public scadaSymbolObjectSettingsFormGroup: UntypedFormGroup; metadata: ScadaSymbolMetadata; behaviorGroups: ScadaSymbolBehaviorGroup[]; - propertyRows: ScadaSymbolPropertyRow[]; constructor(protected store: Store, private fb: UntypedFormBuilder, @@ -119,7 +108,7 @@ export class ScadaSymbolObjectSettingsComponent implements OnInit, OnChanges, Co ngOnInit(): void { this.scadaSymbolObjectSettingsFormGroup = this.fb.group({ behavior: this.fb.group({}), - properties: this.fb.group({}) + properties: [{}, []] }); this.scadaSymbolObjectSettingsFormGroup.valueChanges.subscribe(() => { this.updateModel(); @@ -151,7 +140,6 @@ export class ScadaSymbolObjectSettingsComponent implements OnInit, OnChanges, Co this.scadaSymbolObjectSettingsFormGroup.disable({emitEvent: false}); } else { this.scadaSymbolObjectSettingsFormGroup.enable({emitEvent: false}); - this.updateValidators(); } } @@ -170,12 +158,6 @@ export class ScadaSymbolObjectSettingsComponent implements OnInit, OnChanges, Co } private loadMetadata() { - if (this.validatorSubscription) { - this.validatorSubscription.unsubscribe(); - this.validatorSubscription = null; - } - this.validatorTriggers = []; - let metadata$: Observable; if (this.scadaSymbolMetadata) { metadata$ = of(this.scadaSymbolMetadata); @@ -196,74 +178,19 @@ export class ScadaSymbolObjectSettingsComponent implements OnInit, OnChanges, Co (metadata) => { this.metadata = metadata; this.behaviorGroups = toBehaviorGroups(this.metadata.behavior); - this.propertyRows = toPropertyRows(this.metadata.properties); const behaviorFormGroup = this.scadaSymbolObjectSettingsFormGroup.get('behavior') as UntypedFormGroup; for (const control of Object.keys(behaviorFormGroup.controls)) { behaviorFormGroup.removeControl(control, {emitEvent: false}); } - const propertiesFormGroup = this.scadaSymbolObjectSettingsFormGroup.get('properties') as UntypedFormGroup; - for (const control of Object.keys(propertiesFormGroup.controls)) { - propertiesFormGroup.removeControl(control, {emitEvent: false}); - } for (const behavior of this.metadata.behavior) { behaviorFormGroup.addControl(behavior.id, this.fb.control(null, []), {emitEvent: false}); } - for (const property of this.metadata.properties) { - if (property.disableOnProperty) { - if (!this.validatorTriggers.includes(property.disableOnProperty)) { - this.validatorTriggers.push(property.disableOnProperty); - } - } - const validators: ValidatorFn[] = []; - if (property.required) { - validators.push(Validators.required); - } - if (property.type === ScadaSymbolPropertyType.number) { - if (isDefinedAndNotNull(property.min)) { - validators.push(Validators.min(property.min)); - } - if (isDefinedAndNotNull(property.max)) { - validators.push(Validators.max(property.max)); - } - } - propertiesFormGroup.addControl(property.id, this.fb.control(null, validators), {emitEvent: false}); - } - if (this.validatorTriggers.length) { - const observables: Observable[] = []; - for (const trigger of this.validatorTriggers) { - if (propertiesFormGroup.get(trigger)) { - observables.push(propertiesFormGroup.get(trigger).valueChanges); - } - } - if (observables.length) { - this.validatorSubscription = merge(...observables).subscribe(() => { - this.updateValidators(); - }); - } - } this.setupValue(); this.cd.markForCheck(); } ); } - private updateValidators() { - const propertiesFormGroup = this.scadaSymbolObjectSettingsFormGroup.get('properties') as UntypedFormGroup; - for (const trigger of this.validatorTriggers) { - const value: boolean = propertiesFormGroup.get(trigger).value; - this.metadata.properties.filter(p => p.disableOnProperty === trigger).forEach( - (p) => { - const control = propertiesFormGroup.get(p.id); - if (value) { - control.enable({emitEvent: false}); - } else { - control.disable({emitEvent: false}); - } - } - ); - } - } - private setupValue() { if (this.metadata) { const defaults = defaultScadaSymbolObjectSettings(this.metadata); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/scada/scada-symbol-object-settings.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/scada/scada-symbol-object-settings.models.ts index b884166cd2..2116093c59 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/scada/scada-symbol-object-settings.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/scada/scada-symbol-object-settings.models.ts @@ -15,9 +15,7 @@ /// import { - ScadaSymbolBehavior, - ScadaSymbolProperty, - ScadaSymbolPropertyType + ScadaSymbolBehavior } from '@home/components/widget/lib/scada/scada-symbol.models'; export interface ScadaSymbolBehaviorGroup { @@ -25,13 +23,6 @@ export interface ScadaSymbolBehaviorGroup { behaviors: ScadaSymbolBehavior[]; } -export interface ScadaSymbolPropertyRow { - label: string; - properties: ScadaSymbolProperty[]; - switch?: ScadaSymbolProperty; - rowClass?: string; -} - export const toBehaviorGroups = (behaviors: ScadaSymbolBehavior[]): ScadaSymbolBehaviorGroup[] => { const result: ScadaSymbolBehaviorGroup[] = []; for (const behavior of behaviors) { @@ -54,24 +45,3 @@ export const toBehaviorGroups = (behaviors: ScadaSymbolBehavior[]): ScadaSymbolB } return result; }; - -export const toPropertyRows = (properties: ScadaSymbolProperty[]): ScadaSymbolPropertyRow[] => { - const result: ScadaSymbolPropertyRow[] = []; - for (const property of properties) { - let propertyRow = result.find(r => r.label === property.name); - if (!propertyRow) { - propertyRow = { - label: property.name, - properties: [], - rowClass: property.rowClass - }; - result.push(propertyRow); - } - if (property.type === ScadaSymbolPropertyType.switch) { - propertyRow.switch = property; - } else { - propertyRow.properties.push(property); - } - } - return result; -}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts index 037096f406..1fc32f8c67 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts @@ -164,6 +164,22 @@ import { import { WidgetButtonToggleCustomStylePanelComponent } from '@home/components/widget/lib/settings/common/button/widget-button-toggle-custom-style-panel.component'; +import { + DynamicFormPropertiesComponent +} from '@home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component'; +import { + DynamicFormPropertyRowComponent +} from '@home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-row.component'; +import { + DynamicFormPropertyPanelComponent +} from '@home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component'; +import { DynamicFormComponent } from '@home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component'; +import { + DynamicFormSelectItemsComponent +} from '@home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-items.component'; +import { + DynamicFormSelectItemRowComponent +} from '@home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-item-row.component'; @NgModule({ declarations: [ @@ -226,7 +242,13 @@ import { DataKeyInputComponent, EntityAliasInputComponent, AdvancedRangeComponent, - GradientComponent + GradientComponent, + DynamicFormPropertiesComponent, + DynamicFormPropertyRowComponent, + DynamicFormPropertyPanelComponent, + DynamicFormSelectItemsComponent, + DynamicFormSelectItemRowComponent, + DynamicFormComponent ], imports: [ CommonModule, @@ -293,7 +315,13 @@ import { DataKeyInputComponent, EntityAliasInputComponent, AdvancedRangeComponent, - GradientComponent + GradientComponent, + DynamicFormPropertiesComponent, + DynamicFormPropertyRowComponent, + DynamicFormPropertyPanelComponent, + DynamicFormSelectItemsComponent, + DynamicFormSelectItemRowComponent, + DynamicFormComponent ], providers: [ ColorSettingsComponentService, diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html index b2d1d68ed0..3af9faca69 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html @@ -32,6 +32,9 @@
+
+ +
widget-config.card-title
@@ -310,5 +313,17 @@
+ +
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts index b6c5e02d2b..c9bbcab5d7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts @@ -32,7 +32,7 @@ import { CellClickColumnInfo, DataKey, datasourcesHasAggregation, - datasourcesHasOnlyComparisonAggregation, + datasourcesHasOnlyComparisonAggregation, DynamicFormData, GroupInfo, JsonSchema, JsonSettingsSchema, @@ -190,6 +190,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe public widgetSettings: UntypedFormGroup; public layoutSettings: UntypedFormGroup; public advancedSettings: UntypedFormGroup; + public oldAdvancedSettings: UntypedFormGroup; public actionsSettings: UntypedFormGroup; private createBasicModeComponentTimeout: Timeout; @@ -203,6 +204,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe private widgetSettingsSubscription: Subscription; private layoutSettingsSubscription: Subscription; private advancedSettingsSubscription: Subscription; + private oldAdvancedSettingsSubscription: Subscription; private actionsSettingsSubscription: Subscription; private defaultConfigFormsType: widgetType; @@ -221,6 +223,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe this.dataSettings = this.fb.group({}); this.targetDeviceSettings = this.fb.group({}); this.advancedSettings = this.fb.group({}); + this.oldAdvancedSettings = this.fb.group({}); this.widgetSettings = this.fb.group({ title: [null, []], titleFont: [null, []], @@ -296,6 +299,10 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe this.advancedSettingsSubscription.unsubscribe(); this.advancedSettingsSubscription = null; } + if (this.oldAdvancedSettingsSubscription) { + this.oldAdvancedSettingsSubscription.unsubscribe(); + this.oldAdvancedSettingsSubscription = null; + } if (this.actionsSettingsSubscription) { this.actionsSettingsSubscription.unsubscribe(); this.actionsSettingsSubscription = null; @@ -318,6 +325,9 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe this.advancedSettingsSubscription = this.advancedSettings.valueChanges.subscribe( () => this.updateAdvancedSettings() ); + this.oldAdvancedSettingsSubscription = this.oldAdvancedSettings.valueChanges.subscribe( + () => this.updateOldAdvancedSettings() + ); this.actionsSettingsSubscription = this.actionsSettings.valueChanges.subscribe( () => this.updateActionSettings() ); @@ -340,6 +350,12 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe value: 'appearance' } ); + this.headerOptions.push( + { + name: 'Old appearance', + value: 'oldAppearance' + } + ); } this.headerOptions.push( { @@ -393,6 +409,8 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe } this.advancedSettings.addControl('settings', this.fb.control(null, [])); + this.oldAdvancedSettings.addControl('settings', + this.fb.control(null, [])); } registerOnChange(fn: any): void { @@ -563,7 +581,8 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe } } - this.updateSchemaForm(config.settings); + this.updateAdvancedForm(config.settings); + this.updateSchemaFormOld(config.settings); if (layout) { this.layoutSettings.patchValue( @@ -633,7 +652,19 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe } } - private updateSchemaForm(settings?: any) { + private updateAdvancedForm(settings?: any) { + const dynamicFormData: DynamicFormData = {}; + dynamicFormData.model = settings || {}; + if (this.modelValue.settingsForm?.length) { + dynamicFormData.settingsForm = this.modelValue.settingsForm; + } else { + dynamicFormData.settingsForm = []; + } + dynamicFormData.settingsDirective = this.modelValue.settingsDirective; + this.advancedSettings.patchValue({ settings: dynamicFormData }, {emitEvent: false}); + } + + private updateSchemaFormOld(settings?: any) { const widgetSettingsFormData: JsonFormComponentData = {}; if (this.modelValue.settingsSchema && this.modelValue.settingsSchema.schema) { widgetSettingsFormData.schema = this.modelValue.settingsSchema.schema; @@ -647,7 +678,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe widgetSettingsFormData.model = settings || {}; } widgetSettingsFormData.settingsDirective = this.modelValue.settingsDirective; - this.advancedSettings.patchValue({ settings: widgetSettingsFormData }, {emitEvent: false}); + this.oldAdvancedSettings.patchValue({ settings: widgetSettingsFormData }, {emitEvent: false}); } private updateDataSettings() { @@ -701,6 +732,15 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe } } + private updateOldAdvancedSettings() { + if (this.modelValue) { + if (this.modelValue.config) { + this.modelValue.config.settings = this.advancedSettings.get('settings').value?.model; + } + this.propagateChange(this.modelValue); + } + } + private updateActionSettings() { if (this.modelValue) { if (this.modelValue.config) { diff --git a/ui-ngx/src/app/modules/home/models/widget-component.models.ts b/ui-ngx/src/app/modules/home/models/widget-component.models.ts index 03af49d6d2..31583747bc 100644 --- a/ui-ngx/src/app/modules/home/models/widget-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/widget-component.models.ts @@ -106,6 +106,7 @@ import { UserSettingsService } from '@core/http/user-settings.service'; import { DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models'; import { UtilsService } from '@core/services/utils.service'; import { CompiledTbFunction } from '@shared/models/js-function.models'; +import { FormProperty } from '@shared/models/dynamic-form.models'; export interface IWidgetAction { name: string; @@ -577,6 +578,7 @@ export interface WidgetConfigComponentData { typeParameters: WidgetTypeParameters; actionSources: {[actionSourceId: string]: WidgetActionSource}; isDataEnabled: boolean; + settingsForm: FormProperty[]; settingsSchema: JsonSettingsSchema; dataKeySettingsSchema: JsonSettingsSchema; latestDataKeySettingsSchema: JsonSettingsSchema; diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-components.module.ts b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-components.module.ts index 7507b4cdcb..6e3a02330f 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-components.module.ts +++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-components.module.ts @@ -35,15 +35,6 @@ import { import { ScadaSymbolBehaviorPanelComponent } from '@home/pages/scada-symbol/metadata-components/scada-symbol-behavior-panel.component'; -import { - ScadaSymbolPropertiesComponent -} from '@home/pages/scada-symbol/metadata-components/scada-symbol-properties.component'; -import { - ScadaSymbolPropertyRowComponent -} from '@home/pages/scada-symbol/metadata-components/scada-symbol-property-row.component'; -import { - ScadaSymbolPropertyPanelComponent -} from '@home/pages/scada-symbol/metadata-components/scada-symbol-property-panel.component'; import { WidgetSettingsCommonModule } from '@home/components/widget/lib/settings/common/widget-settings-common.module'; import { ScadaSymbolMetadataTagFunctionPanelComponent @@ -58,10 +49,7 @@ import { ScadaSymbolMetadataTagFunctionPanelComponent, ScadaSymbolBehaviorsComponent, ScadaSymbolBehaviorRowComponent, - ScadaSymbolBehaviorPanelComponent, - ScadaSymbolPropertiesComponent, - ScadaSymbolPropertyRowComponent, - ScadaSymbolPropertyPanelComponent + ScadaSymbolBehaviorPanelComponent ], imports: [ CommonModule, diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.html b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.html index c505546286..00a18cc2ab 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.html +++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.html @@ -106,9 +106,9 @@
- - +
diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-property-panel.component.ts b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-property-panel.component.ts deleted file mode 100644 index 4c59787c0f..0000000000 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-property-panel.component.ts +++ /dev/null @@ -1,136 +0,0 @@ -/// -/// 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, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; -import { TbPopoverComponent } from '@shared/components/popover.component'; -import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; -import { - ScadaSymbolProperty, - scadaSymbolPropertyFieldClasses, - scadaSymbolPropertyRowClasses, - ScadaSymbolPropertyType, - scadaSymbolPropertyTypes, - scadaSymbolPropertyTypeTranslations -} from '@home/components/widget/lib/scada/scada-symbol.models'; -import { defaultPropertyValue } from '@home/pages/scada-symbol/metadata-components/scada-symbol-property-row.component'; -import { ValueType } from '@shared/models/constants'; - -@Component({ - selector: 'tb-scada-symbol-property-panel', - templateUrl: './scada-symbol-property-panel.component.html', - styleUrls: ['./scada-symbol-property-panel.component.scss'], - encapsulation: ViewEncapsulation.None -}) -export class ScadaSymbolPropertyPanelComponent implements OnInit { - - ValueType = ValueType; - - ScadaSymbolPropertyType = ScadaSymbolPropertyType; - - scadaSymbolPropertyTypes = scadaSymbolPropertyTypes; - scadaSymbolPropertyTypeTranslations = scadaSymbolPropertyTypeTranslations; - - scadaSymbolPropertyRowClasses = scadaSymbolPropertyRowClasses; - - scadaSymbolPropertyFieldClasses = scadaSymbolPropertyFieldClasses; - - @Input() - isAdd = false; - - @Input() - property: ScadaSymbolProperty; - - @Input() - booleanPropertyIds: string[]; - - @Input() - disabled: boolean; - - @Input() - popover: TbPopoverComponent; - - @Output() - propertySettingsApplied = new EventEmitter(); - - panelTitle: string; - - propertyFormGroup: UntypedFormGroup; - - private propertyType: ScadaSymbolPropertyType; - - constructor(private fb: UntypedFormBuilder) { - } - - ngOnInit(): void { - this.panelTitle = this.isAdd ? 'scada.property.add-property' : 'scada.property.property-settings'; - this.propertyType = this.property.type; - this.propertyFormGroup = this.fb.group( - { - id: [this.property.id, [Validators.required]], - name: [this.property.name, [Validators.required]], - type: [this.property.type, [Validators.required]], - default: [this.property.default, []], - required: [this.property.required, []], - subLabel: [this.property.subLabel, []], - divider: [this.property.divider, []], - fieldSuffix: [this.property.fieldSuffix, []], - disableOnProperty: [this.property.disableOnProperty, []], - rowClass: [(this.property.rowClass || '').split(' '), []], - fieldClass: [(this.property.fieldClass || '').split(' '), []], - min: [this.property.min, []], - max: [this.property.max, []], - step: [this.property.step, [Validators.min(0)]] - } - ); - if (this.disabled) { - this.propertyFormGroup.disable({emitEvent: false}); - } else { - this.propertyFormGroup.get('type').valueChanges.subscribe(() => { - this.updateValidators(); - }); - this.updateValidators(); - } - } - - cancel() { - this.popover?.hide(); - } - - applyPropertySettings() { - const property = this.propertyFormGroup.getRawValue(); - property.rowClass = (property.rowClass || []).join(' '); - property.fieldClass = (property.fieldClass || []).join(' '); - this.propertySettingsApplied.emit(property); - } - - private updateValidators() { - const type: ScadaSymbolPropertyType = this.propertyFormGroup.get('type').value; - if (type === ScadaSymbolPropertyType.number) { - this.propertyFormGroup.get('min').enable({emitEvent: false}); - this.propertyFormGroup.get('max').enable({emitEvent: false}); - this.propertyFormGroup.get('step').enable({emitEvent: false}); - } else { - this.propertyFormGroup.get('min').disable({emitEvent: false}); - this.propertyFormGroup.get('max').disable({emitEvent: false}); - this.propertyFormGroup.get('step').disable({emitEvent: false}); - } - if (this.propertyType !== type) { - const defaultValue = defaultPropertyValue(type); - this.propertyFormGroup.get('default').patchValue(defaultValue, {emitEvent: false}); - this.propertyType = type; - } - } -} diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.models.ts b/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.models.ts index 71fb7f59a2..9fe747db0a 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.models.ts +++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.models.ts @@ -27,14 +27,13 @@ import { ScadaSymbolBehavior, ScadaSymbolBehaviorType, scadaSymbolContentData, - ScadaSymbolMetadata, - ScadaSymbolProperty, - ScadaSymbolPropertyType + ScadaSymbolMetadata } from '@home/components/widget/lib/scada/scada-symbol.models'; import { TbEditorCompletion, TbEditorCompletions } from '@shared/models/ace/completion.models'; import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe'; import { AceHighlightRule, AceHighlightRules } from '@shared/models/ace/ace.models'; import { HelpLinks, ValueType } from '@shared/models/constants'; +import { formPropertyCompletions } from '@shared/models/dynamic-form.models'; import ITooltipsterInstance = JQueryTooltipster.ITooltipsterInstance; import TooltipPositioningSide = JQueryTooltipster.TooltipPositioningSide; import ITooltipsterHelper = JQueryTooltipster.ITooltipsterHelper; @@ -1141,11 +1140,8 @@ export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags meta: 'object', type: 'object', description: 'An object holding all defined SCADA symbol properties.', - children: {} + children: formPropertyCompletions(metadata.properties, customTranslate) }; - for (const property of metadata.properties) { - properties.children[property.id] = scadaSymbolPropertyCompletion(property, customTranslate); - } const values: TbEditorCompletion = { meta: 'object', type: 'object', @@ -1441,18 +1437,6 @@ export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags }; }; -const scadaSymbolPropertyCompletion = (property: ScadaSymbolProperty, customTranslate: CustomTranslatePipe): TbEditorCompletion => { - let description = customTranslate.transform(property.name, property.name); - if (property.subLabel) { - description += ` ${customTranslate.transform(property.subLabel, property.subLabel)}`; - } - return { - meta: 'property', - description, - type: scadaSymbolPropertyCompletionType(property.type) - }; -}; - const scadaSymbolValueCompletion = (value: ScadaSymbolBehavior, customTranslate: CustomTranslatePipe): TbEditorCompletion => { const description = customTranslate.transform(value.name, value.name); return { @@ -1462,27 +1446,6 @@ const scadaSymbolValueCompletion = (value: ScadaSymbolBehavior, customTranslate: }; }; -const scadaSymbolPropertyCompletionType = (type: ScadaSymbolPropertyType): string => { - switch (type) { - case ScadaSymbolPropertyType.text: - return 'string'; - case ScadaSymbolPropertyType.number: - return 'number'; - case ScadaSymbolPropertyType.switch: - return 'boolean'; - case ScadaSymbolPropertyType.color: - return 'color string'; - case ScadaSymbolPropertyType.color_settings: - return 'ColorProcessor'; - case ScadaSymbolPropertyType.font: - return 'Font'; - case ScadaSymbolPropertyType.units: - return 'units string'; - case ScadaSymbolPropertyType.icon: - return 'icon string'; - } -}; - const scadaSymbolValueCompletionType = (type: ValueType): string => { switch (type) { case ValueType.STRING: diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html index 416297a80a..6f77256e9e 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html @@ -188,6 +188,15 @@
+ +
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.ts b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.ts index 03b5674268..0a3d711dee 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.ts +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.ts @@ -21,8 +21,10 @@ import { EventEmitter, Inject, OnDestroy, - OnInit, Renderer2, - ViewChild, ViewContainerRef, + OnInit, + Renderer2, + ViewChild, + ViewContainerRef, ViewEncapsulation } from '@angular/core'; import { Store } from '@ngrx/store'; @@ -65,12 +67,13 @@ import { Observable } from 'rxjs/internal/Observable'; import { catchError, map, tap } from 'rxjs/operators'; import { beautifyCss, beautifyHtml, beautifyJs } from '@shared/models/beautify.models'; import { HttpClient, HttpStatusCode } from '@angular/common/http'; -import Timeout = NodeJS.Timeout; -import { TbEditorCompleter } from '@shared/models/ace/completion.models'; import { loadModulesCompleter } from '@shared/models/js-function.models'; import { TbPopoverService } from '@shared/components/popover.service'; import { JsFuncModulesComponent } from '@shared/components/js-func-modules.component'; import { MatIconButton } from '@angular/material/button'; +import { formPropertyCompletions, jsonFormSchemaToFormProperties } from '@shared/models/dynamic-form.models'; +import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe'; +import Timeout = NodeJS.Timeout; // @dynamic @Component({ @@ -197,6 +200,7 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe private popoverService: TbPopoverService, private renderer: Renderer2, private viewContainerRef: ViewContainerRef, + private customTranslate: CustomTranslatePipe, private http: HttpClient) { super(store); @@ -223,6 +227,9 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe const config = JSON.parse(this.widget.defaultConfig); this.widget.defaultConfig = JSON.stringify(config); } + if (!this.widget.settingsForm?.length) { + this.widget.settingsForm = jsonFormSchemaToFormProperties(this.widget.settingsSchema); + } this.origWidget = deepClone(this.widget); if (!this.widgetTypeDetails) { this.isDirty = true; @@ -405,6 +412,11 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe this.jsEditor.on('change', () => { this.cleanupJsErrors(); }); + if (!(this.jsEditor as any).completer) { + this.jsEditor.execCommand("startAutocomplete"); + (this.jsEditor as any).completer.detach(); + } + (this.jsEditor as any).completer.popup.container.style.width = '500px'; this.initialCompleters = this.jsEditor.completers || []; }) )); @@ -873,6 +885,11 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe this.isDirty = true; } + settingsFormUpdated() { + this.isDirty = true; + this.updateControllerScriptCompleters(); + } + editControllerScriptModules($event: Event, button: MatIconButton) { if ($event) { $event.stopPropagation(); @@ -951,7 +968,8 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe const modulesCompleterObservable = loadModulesCompleter(this.http, this.controllerScriptModules); modulesCompleterObservable.subscribe((modulesCompleter) => { const completers: Ace.Completer[] = []; - completers.push(widgetEditorCompleter); + const formPropertiesCompletions = formPropertyCompletions(this.widget.settingsForm || [], this.customTranslate); + completers.push(widgetEditorCompleter(formPropertiesCompletions)); if (modulesCompleter) { completers.push(modulesCompleter); } diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.models.ts b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.models.ts index 5d4afd93fc..b8175704ca 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.models.ts +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.models.ts @@ -15,77 +15,84 @@ /// import { TbEditorCompleter, TbEditorCompletions } from '@shared/models/ace/completion.models'; -import { widgetContextCompletions } from '@shared/models/ace/widget-completion.models'; +import { + widgetContextCompletions, + widgetContextCompletionsWithSettings +} from '@shared/models/ace/widget-completion.models'; import { serviceCompletions } from '@shared/models/ace/service-completion.models'; -const widgetEditorCompletions: TbEditorCompletions = { - ... {self: { - description: 'Built-in variable self that is a reference to the widget instance', - type: 'WidgetTypeInstance', - meta: 'object', - children: { - ...{ - onInit: { - description: 'The first function which is called when widget is ready for initialization.
Should be used to prepare widget DOM, process widget settings and initial subscription information.', - meta: 'function' - }, - onDataUpdated: { - description: 'Called when the new data is available from the widget subscription.
Latest data can be accessed from ' + - 'the defaultSubscription property of widget context (ctx).', - meta: 'function' - }, - onResize: { - description: 'Called when widget container is resized. Latest width and height can be obtained from widget context (ctx).', - meta: 'function' - }, - onEditModeChanged: { - description: 'Called when dashboard editing mode is changed. Latest mode is handled by isEdit property of widget context (ctx).', - meta: 'function' - }, - onMobileModeChanged: { - description: 'Called when dashboard view width crosses mobile breakpoint. Latest state is handled by isMobile property of widget context (ctx).', - meta: 'function' - }, - onDestroy: { - description: 'Called when widget element is destroyed. Should be used to cleanup all resources if necessary.', - meta: 'function' - }, - getSettingsSchema: { - description: 'Optional function returning widget settings schema json as alternative to Settings tab of Settings schema section.', - meta: 'function', - return: { - description: 'An widget settings schema json', - type: 'object' - } - }, - getDataKeySettingsSchema: { - description: 'Optional function returning particular data key settings schema json as alternative to Data key settings schema of Settings schema section.', - meta: 'function', - return: { - description: 'A particular data key settings schema json', - type: 'object' - } - }, - typeParameters: { - description: 'Returns object describing widget datasource parameters.', - meta: 'function', - return: { - description: 'An object describing widget datasource parameters.', - type: 'WidgetTypeParameters' - } - }, - actionSources: { - description: 'Returns map describing available widget action sources used to define user actions.', - meta: 'function', - return: { - description: 'A map of action sources by action source id.', - type: '{[actionSourceId: string]: WidgetActionSource}' - } +const widgetEditorCompletions = (settingsCompletions?: TbEditorCompletions): TbEditorCompletions => { + return { + ... {self: { + description: 'Built-in variable self that is a reference to the widget instance', + type: 'WidgetTypeInstance', + meta: 'object', + children: { + ...{ + onInit: { + description: 'The first function which is called when widget is ready for initialization.
Should be used to prepare widget DOM, process widget settings and initial subscription information.', + meta: 'function' + }, + onDataUpdated: { + description: 'Called when the new data is available from the widget subscription.
Latest data can be accessed from ' + + 'the defaultSubscription property of widget context (ctx).', + meta: 'function' + }, + onResize: { + description: 'Called when widget container is resized. Latest width and height can be obtained from widget context (ctx).', + meta: 'function' + }, + onEditModeChanged: { + description: 'Called when dashboard editing mode is changed. Latest mode is handled by isEdit property of widget context (ctx).', + meta: 'function' + }, + onMobileModeChanged: { + description: 'Called when dashboard view width crosses mobile breakpoint. Latest state is handled by isMobile property of widget context (ctx).', + meta: 'function' + }, + onDestroy: { + description: 'Called when widget element is destroyed. Should be used to cleanup all resources if necessary.', + meta: 'function' + }, + getSettingsSchema: { + description: 'Optional function returning widget settings schema json as alternative to Settings tab of Settings schema section.', + meta: 'function', + return: { + description: 'An widget settings schema json', + type: 'object' + } + }, + getDataKeySettingsSchema: { + description: 'Optional function returning particular data key settings schema json as alternative to Data key settings schema of Settings schema section.', + meta: 'function', + return: { + description: 'A particular data key settings schema json', + type: 'object' + } + }, + typeParameters: { + description: 'Returns object describing widget datasource parameters.', + meta: 'function', + return: { + description: 'An object describing widget datasource parameters.', + type: 'WidgetTypeParameters' + } + }, + actionSources: { + description: 'Returns map describing available widget action sources used to define user actions.', + meta: 'function', + return: { + description: 'A map of action sources by action source id.', + type: '{[actionSourceId: string]: WidgetActionSource}' + } + } + }, + ...widgetContextCompletionsWithSettings(settingsCompletions) } - }, - ...widgetContextCompletions - } - }} + }} + } }; -export const widgetEditorCompleter = new TbEditorCompleter(widgetEditorCompletions); +export const widgetEditorCompleter = (settingsCompletions?: TbEditorCompletions): TbEditorCompleter => { + return new TbEditorCompleter(widgetEditorCompletions(settingsCompletions)); +} diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-library.module.ts b/ui-ngx/src/app/modules/home/pages/widget/widget-library.module.ts index 80438b4f4d..f76be11ba1 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widget-library.module.ts +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-library.module.ts @@ -29,6 +29,7 @@ import { WidgetTypeTabsComponent } from '@home/pages/widget/widget-type-tabs.com import { WidgetsBundleWidgetsComponent } from '@home/pages/widget/widgets-bundle-widgets.component'; import { WidgetTypeAutocompleteComponent } from '@home/pages/widget/widget-type-autocomplete.component'; import { WidgetsBundleDialogComponent } from '@home/pages/widget/widgets-bundle-dialog.component'; +import { WidgetConfigComponentsModule } from '@home/components/widget/config/widget-config-components.module'; @NgModule({ declarations: [ @@ -47,6 +48,7 @@ import { WidgetsBundleDialogComponent } from '@home/pages/widget/widgets-bundle- CommonModule, SharedModule, HomeComponentsModule, + WidgetConfigComponentsModule, WidgetLibraryRoutingModule ] }) diff --git a/ui-ngx/src/app/shared/components/value-input.component.html b/ui-ngx/src/app/shared/components/value-input.component.html index e4aa511e83..e6138dd321 100644 --- a/ui-ngx/src/app/shared/components/value-input.component.html +++ b/ui-ngx/src/app/shared/components/value-input.component.html @@ -68,11 +68,16 @@ warning - - {{ trueLabel }} - {{ falseLabel }} - + + + {{ trueLabel }} + {{ falseLabel }} + + + {{ modelValue ? trueLabel : falseLabel }} + +
and data used by widget instance.', - meta: 'object', - type: 'WidgetContext', - children: { - ...{ - $container: { - description: 'Container element of the widget.
Can be used to dynamically access or modify widget DOM using jQuery API.', - meta: 'property', - type: 'jQuery Object' - }, - $scope: { - description: 'Reference to the current widget component.
Can be used to access/modify component properties when widget is built using Angular approach.', - meta: 'property', - type: 'IDynamicWidgetComponent' - }, - width: { - description: 'Current width of widget container in pixels.', - meta: 'property', - type: 'number' - }, - height: { - description: 'Current height of widget container in pixels.', - meta: 'property', - type: 'number' - }, - isEdit: { - description: 'Indicates whether the dashboard is in in the view or editing state.', - meta: 'property', - type: 'boolean' - }, - isMobile: { - description: 'Indicates whether the dashboard view is less then 960px width (default mobile breakpoint).', - meta: 'property', - type: 'boolean' - }, - widgetConfig: { - description: 'Common widget configuration containing properties such as color (text color), backgroundColor (widget background color), etc.', - meta: 'property', - type: 'WidgetConfig', - children: { - title: { - description: 'Widget title.', - meta: 'property', - type: 'string' - }, - titleIcon: { - description: 'Widget title icon.', - meta: 'property', - type: 'string' - }, - showTitle: { - description: 'Whether to show widget title.', - meta: 'property', - type: 'boolean' - }, - showTitleIcon: { - description: 'Whether to show widget title icon.', - meta: 'property', - type: 'boolean' - }, - iconColor: { - description: 'Widget title icon color.', - meta: 'property', - type: 'string' - }, - iconSize: { - description: 'Widget title icon size.', - meta: 'property', - type: 'string' - }, - titleTooltip: { - description: 'Widget title tooltip content.', - meta: 'property', - type: 'string' - }, - dropShadow: { - description: 'Enable/disable widget card shadow.', - meta: 'property', - type: 'boolean' - }, - enableFullscreen: { - description: 'Whether to enable fullscreen button on widget.', - meta: 'property', - type: 'boolean' - }, - useDashboardTimewindow: { - description: 'Whether to use dashboard timewindow (applicable for timeseries widgets).', - meta: 'property', - type: 'boolean' - }, - displayTimewindow: { - description: 'Whether to display timewindow (applicable for timeseries widgets).', - meta: 'property', - type: 'boolean' - }, - showLegend: { - description: 'Whether to show legend.', - meta: 'property', - type: 'boolean' - }, - legendConfig: { - description: 'Legend configuration.', - meta: 'property', - type: 'LegendConfig', - children: { - position: { - description: 'Legend position. Possible values: \'top\', \'bottom\', \'left\', \'right\'', - meta: 'property', - type: 'LegendPosition', - }, - direction: { - description: 'Legend direction. Possible values: \'column\', \'row\'', - meta: 'property', - type: 'LegendDirection', - }, - showMin: { - description: 'Whether to display aggregated min values.', - meta: 'property', - type: 'boolean', - }, - showMax: { - description: 'Whether to display aggregated max values.', - meta: 'property', - type: 'boolean', - }, - showAvg: { - description: 'Whether to display aggregated average values.', - meta: 'property', - type: 'boolean', - }, - showTotal: { - description: 'Whether to display aggregated total values.', - meta: 'property', - type: 'boolean', +export const widgetContextCompletionsWithSettings = (settingsCompletions?: TbEditorCompletions): TbEditorCompletions => { + return { + ctx: { + description: 'A reference to widget context that has all necessary API
and data used by widget instance.', + meta: 'object', + type: 'WidgetContext', + children: { + ...{ + $container: { + description: 'Container element of the widget.
Can be used to dynamically access or modify widget DOM using jQuery API.', + meta: 'property', + type: 'jQuery Object' + }, + $scope: { + description: 'Reference to the current widget component.
Can be used to access/modify component properties when widget is built using Angular approach.', + meta: 'property', + type: 'IDynamicWidgetComponent' + }, + width: { + description: 'Current width of widget container in pixels.', + meta: 'property', + type: 'number' + }, + height: { + description: 'Current height of widget container in pixels.', + meta: 'property', + type: 'number' + }, + isEdit: { + description: 'Indicates whether the dashboard is in in the view or editing state.', + meta: 'property', + type: 'boolean' + }, + isMobile: { + description: 'Indicates whether the dashboard view is less then 960px width (default mobile breakpoint).', + meta: 'property', + type: 'boolean' + }, + widgetConfig: { + description: 'Common widget configuration containing properties such as color (text color), backgroundColor (widget background color), etc.', + meta: 'property', + type: 'WidgetConfig', + children: { + title: { + description: 'Widget title.', + meta: 'property', + type: 'string' + }, + titleIcon: { + description: 'Widget title icon.', + meta: 'property', + type: 'string' + }, + showTitle: { + description: 'Whether to show widget title.', + meta: 'property', + type: 'boolean' + }, + showTitleIcon: { + description: 'Whether to show widget title icon.', + meta: 'property', + type: 'boolean' + }, + iconColor: { + description: 'Widget title icon color.', + meta: 'property', + type: 'string' + }, + iconSize: { + description: 'Widget title icon size.', + meta: 'property', + type: 'string' + }, + titleTooltip: { + description: 'Widget title tooltip content.', + meta: 'property', + type: 'string' + }, + dropShadow: { + description: 'Enable/disable widget card shadow.', + meta: 'property', + type: 'boolean' + }, + enableFullscreen: { + description: 'Whether to enable fullscreen button on widget.', + meta: 'property', + type: 'boolean' + }, + useDashboardTimewindow: { + description: 'Whether to use dashboard timewindow (applicable for timeseries widgets).', + meta: 'property', + type: 'boolean' + }, + displayTimewindow: { + description: 'Whether to display timewindow (applicable for timeseries widgets).', + meta: 'property', + type: 'boolean' + }, + showLegend: { + description: 'Whether to show legend.', + meta: 'property', + type: 'boolean' + }, + legendConfig: { + description: 'Legend configuration.', + meta: 'property', + type: 'LegendConfig', + children: { + position: { + description: 'Legend position. Possible values: \'top\', \'bottom\', \'left\', \'right\'', + meta: 'property', + type: 'LegendPosition', + }, + direction: { + description: 'Legend direction. Possible values: \'column\', \'row\'', + meta: 'property', + type: 'LegendDirection', + }, + showMin: { + description: 'Whether to display aggregated min values.', + meta: 'property', + type: 'boolean', + }, + showMax: { + description: 'Whether to display aggregated max values.', + meta: 'property', + type: 'boolean', + }, + showAvg: { + description: 'Whether to display aggregated average values.', + meta: 'property', + type: 'boolean', + }, + showTotal: { + description: 'Whether to display aggregated total values.', + meta: 'property', + type: 'boolean', + } } + }, + timewindow: timewindowCompletion, + mobileHeight: { + description: 'Widget height in mobile mode.', + meta: 'property', + type: 'number' + }, + mobileOrder: { + description: 'Widget order in mobile mode.', + meta: 'property', + type: 'number' + }, + color: { + description: 'Widget text color.', + meta: 'property', + type: 'string' + }, + backgroundColor: { + description: 'Widget background color.', + meta: 'property', + type: 'string' + }, + padding: { + description: 'Widget card padding.', + meta: 'property', + type: 'string' + }, + margin: { + description: 'Widget card margin.', + meta: 'property', + type: 'string' + }, + widgetStyle: { + description: 'Widget element style object.', + meta: 'property', + type: 'object' + }, + titleStyle: { + description: 'Widget title element style object.', + meta: 'property', + type: 'object' + }, + units: { + description: 'Optional property defining units text of values displayed by widget. Useful for simple widgets like cards or gauges.', + meta: 'property', + type: 'string' + }, + decimals: { + description: 'Optional property defining how many positions should be used to display decimal part of the value number.', + meta: 'property', + type: 'number' + }, + actions: { + description: 'Map of configured widget actions.', + meta: 'property', + type: 'object' + }, + settings: { + description: 'Widget settings containing widget specific properties according to the defined settings json schema', + meta: 'property', + type: 'object', + children: settingsCompletions + }, + alarmSource: { + description: 'Configured alarm source for alarm widget type.', + meta: 'property', + type: 'Datasource' + }, + alarmSearchStatus: { + description: 'Configured default alarm search status for alarm widget type.', + meta: 'property', + type: 'AlarmSearchStatus' + }, + alarmsPollingInterval: { + description: 'Configured alarms polling interval for alarm widget type.', + meta: 'property', + type: 'number' + }, + alarmsMaxCountLoad: { + description: 'Configured maximum alarms to load for alarm widget type.', + meta: 'property', + type: 'number' + }, + alarmsFetchSize: { + description: 'Configured alarms page size used to load alarms.', + meta: 'property', + type: 'number' + }, + datasources: { + description: 'Array of configured widget datasources.', + meta: 'property', + type: 'Array<Datasource>' } - }, - timewindow: timewindowCompletion, - mobileHeight: { - description: 'Widget height in mobile mode.', - meta: 'property', - type: 'number' - }, - mobileOrder: { - description: 'Widget order in mobile mode.', - meta: 'property', - type: 'number' - }, - color: { - description: 'Widget text color.', - meta: 'property', - type: 'string' - }, - backgroundColor: { - description: 'Widget background color.', - meta: 'property', - type: 'string' - }, - padding: { - description: 'Widget card padding.', - meta: 'property', - type: 'string' - }, - margin: { - description: 'Widget card margin.', - meta: 'property', - type: 'string' - }, - widgetStyle: { - description: 'Widget element style object.', - meta: 'property', - type: 'object' - }, - titleStyle: { - description: 'Widget title element style object.', - meta: 'property', - type: 'object' - }, - units: { - description: 'Optional property defining units text of values displayed by widget. Useful for simple widgets like cards or gauges.', - meta: 'property', - type: 'string' - }, - decimals: { - description: 'Optional property defining how many positions should be used to display decimal part of the value number.', - meta: 'property', - type: 'number' - }, - actions: { - description: 'Map of configured widget actions.', - meta: 'property', - type: 'object' - }, - settings: { - description: 'Object holding widget settings according to widget type.', - meta: 'property', - type: 'object' - }, - alarmSource: { - description: 'Configured alarm source for alarm widget type.', - meta: 'property', - type: 'Datasource' - }, - alarmSearchStatus: { - description: 'Configured default alarm search status for alarm widget type.', - meta: 'property', - type: 'AlarmSearchStatus' - }, - alarmsPollingInterval: { - description: 'Configured alarms polling interval for alarm widget type.', - meta: 'property', - type: 'number' - }, - alarmsMaxCountLoad: { - description: 'Configured maximum alarms to load for alarm widget type.', - meta: 'property', - type: 'number' - }, - alarmsFetchSize: { - description: 'Configured alarms page size used to load alarms.', - meta: 'property', - type: 'number' - }, - datasources: { - description: 'Array of configured widget datasources.', - meta: 'property', - type: 'Array<Datasource>' } - } - }, - settings: { - description: 'Widget settings containing widget specific properties according to the defined settings json schema', - meta: 'property', - type: 'object' - }, - datasources: { - description: 'Array of resolved widget datasources.', - meta: 'property', - type: 'Array<Datasource>' - }, - data: { - description: 'Array of latest datasources data.', - meta: 'property', - type: 'Array<DatasourceData>' - }, - timeWindow: { - description: 'Current widget timewindow (applicable for timeseries widgets).', - meta: 'property', - type: 'WidgetTimewindow' - }, - units: { - description: 'Optional property defining units text of values displayed by widget. Useful for simple widgets like cards or gauges.', - meta: 'property', - type: 'string' - }, - decimals: { - description: 'Optional property defining how many positions should be used to display decimal part of the value number.', - meta: 'property', - type: 'number' - }, - currentUser: { - description: 'Current user object.', - meta: 'property', - type: 'AuthUser', - children: { - sub: { - description: 'User subject (email).', - meta: 'property', - type: 'string' - }, - scopes: { - description: 'User security scopes.', - meta: 'property', - type: 'Array' - }, - userId: { - description: 'User id.', - meta: 'property', - type: 'string' - }, - firstName: { - description: 'User first name.', - meta: 'property', - type: 'string' - }, - lastName: { - description: 'User last name.', - meta: 'property', - type: 'string' - }, - enabled: { - description: 'Whether is user enabled.', - meta: 'property', - type: 'boolean' - }, - tenantId: { - description: 'Tenant id of the user.', - meta: 'property', - type: 'string' - }, - customerId: { - description: 'Customer id of the user (available when user belongs to specific customer).', - meta: 'property', - type: 'string' - }, - isPublic: { - description: 'Special flag indicating public user.', - meta: 'property', - type: 'boolean' - }, - authority: { - description: 'User authority. Possible values: SYS_ADMIN, TENANT_ADMIN, CUSTOMER_USER', - meta: 'property', - type: 'Authority' + }, + settings: { + description: 'Widget settings containing widget specific properties according to the defined settings json schema', + meta: 'property', + type: 'object', + children: settingsCompletions + }, + datasources: { + description: 'Array of resolved widget datasources.', + meta: 'property', + type: 'Array<Datasource>' + }, + data: { + description: 'Array of latest datasources data.', + meta: 'property', + type: 'Array<DatasourceData>' + }, + timeWindow: { + description: 'Current widget timewindow (applicable for timeseries widgets).', + meta: 'property', + type: 'WidgetTimewindow' + }, + units: { + description: 'Optional property defining units text of values displayed by widget. Useful for simple widgets like cards or gauges.', + meta: 'property', + type: 'string' + }, + decimals: { + description: 'Optional property defining how many positions should be used to display decimal part of the value number.', + meta: 'property', + type: 'number' + }, + currentUser: { + description: 'Current user object.', + meta: 'property', + type: 'AuthUser', + children: { + sub: { + description: 'User subject (email).', + meta: 'property', + type: 'string' + }, + scopes: { + description: 'User security scopes.', + meta: 'property', + type: 'Array' + }, + userId: { + description: 'User id.', + meta: 'property', + type: 'string' + }, + firstName: { + description: 'User first name.', + meta: 'property', + type: 'string' + }, + lastName: { + description: 'User last name.', + meta: 'property', + type: 'string' + }, + enabled: { + description: 'Whether is user enabled.', + meta: 'property', + type: 'boolean' + }, + tenantId: { + description: 'Tenant id of the user.', + meta: 'property', + type: 'string' + }, + customerId: { + description: 'Customer id of the user (available when user belongs to specific customer).', + meta: 'property', + type: 'string' + }, + isPublic: { + description: 'Special flag indicating public user.', + meta: 'property', + type: 'boolean' + }, + authority: { + description: 'User authority. Possible values: SYS_ADMIN, TENANT_ADMIN, CUSTOMER_USER', + meta: 'property', + type: 'Authority' + } } - } - }, - hideTitlePanel: { - description: 'Manages visibility of widget title panel. Useful for widget with custom title panels or different states. updateWidgetParams() function must be called after this property change.', - meta: 'property', - type: 'boolean' - }, - widgetTitle: { - description: 'If set, will override configured widget title text. updateWidgetParams() function must be called after this property change.', - meta: 'property', - type: 'string' - }, - detectChanges: { - description: 'Trigger change detection for current widget. Must be invoked when widget HTML template bindings should be updated due to widget data changes.', - meta: 'function' - }, - updateWidgetParams: { - description: 'Updates widget with runtime set properties such as widgetTitle, hideTitlePanel, etc. Must be invoked in order these properties changes take effect.', - meta: 'function' - }, - defaultSubscription: { - description: 'Default widget subscription object contains all subscription information,
including current data, according to the widget type.', - meta: 'property', - type: 'IWidgetSubscription' - }, - timewindowFunctions: { - description: 'Object with timewindow functions used to manage widget data time frame. Can by used by Time-series or Alarm widgets.', - meta: 'property', - type: 'TimewindowFunctions', - children: { - onUpdateTimewindow: { - description: 'This function can be used to update current subscription time frame
to historical one identified by startTimeMs and endTimeMs arguments.', - meta: 'function', - args: [ - { - name: 'startTimeMs', - description: 'Timewindow start time in UTC milliseconds', - type: 'number' - }, - { - name: 'endTimeMs', - description: 'Timewindow end time in UTC milliseconds', - type: 'number' - } - ] - }, - onResetTimewindow: { - description: 'Resets subscription time frame to default defined by widget timewindow component
or dashboard timewindow depending on widget settings.', - meta: 'function' + }, + hideTitlePanel: { + description: 'Manages visibility of widget title panel. Useful for widget with custom title panels or different states. updateWidgetParams() function must be called after this property change.', + meta: 'property', + type: 'boolean' + }, + widgetTitle: { + description: 'If set, will override configured widget title text. updateWidgetParams() function must be called after this property change.', + meta: 'property', + type: 'string' + }, + detectChanges: { + description: 'Trigger change detection for current widget. Must be invoked when widget HTML template bindings should be updated due to widget data changes.', + meta: 'function' + }, + updateWidgetParams: { + description: 'Updates widget with runtime set properties such as widgetTitle, hideTitlePanel, etc. Must be invoked in order these properties changes take effect.', + meta: 'function' + }, + defaultSubscription: { + description: 'Default widget subscription object contains all subscription information,
including current data, according to the widget type.', + meta: 'property', + type: 'IWidgetSubscription' + }, + timewindowFunctions: { + description: 'Object with timewindow functions used to manage widget data time frame. Can by used by Time-series or Alarm widgets.', + meta: 'property', + type: 'TimewindowFunctions', + children: { + onUpdateTimewindow: { + description: 'This function can be used to update current subscription time frame
to historical one identified by startTimeMs and endTimeMs arguments.', + meta: 'function', + args: [ + { + name: 'startTimeMs', + description: 'Timewindow start time in UTC milliseconds', + type: 'number' + }, + { + name: 'endTimeMs', + description: 'Timewindow end time in UTC milliseconds', + type: 'number' + } + ] + }, + onResetTimewindow: { + description: 'Resets subscription time frame to default defined by widget timewindow component
or dashboard timewindow depending on widget settings.', + meta: 'function' + } } - } - }, - controlApi: { - description: 'Object that provides API functions for RPC (Control) widgets.', - meta: 'property', - type: 'RpcApi', - children: { - sendOneWayCommand: { - description: 'Sends one way (without response) RPC command to the device.', - meta: 'function', - args: [ - { - name: 'method', - description: 'RPC method name', - type: 'string' - }, - { - name: 'params', - description: 'RPC method params, custom json object', - type: 'object', - optional: true - }, - { - name: 'timeout', - description: 'Maximum delay in milliseconds to wait until response/acknowledgement is received.', - type: 'number', - optional: true - }, - { - name: 'persistent', - description: 'RPC request persistent', - type: 'boolean', - optional: true - }, - { - name: 'persistentPollingInterval', - description: 'Polling interval in milliseconds to get persistent RPC command response', - type: 'number', - optional: true + }, + controlApi: { + description: 'Object that provides API functions for RPC (Control) widgets.', + meta: 'property', + type: 'RpcApi', + children: { + sendOneWayCommand: { + description: 'Sends one way (without response) RPC command to the device.', + meta: 'function', + args: [ + { + name: 'method', + description: 'RPC method name', + type: 'string' + }, + { + name: 'params', + description: 'RPC method params, custom json object', + type: 'object', + optional: true + }, + { + name: 'timeout', + description: 'Maximum delay in milliseconds to wait until response/acknowledgement is received.', + type: 'number', + optional: true + }, + { + name: 'persistent', + description: 'RPC request persistent', + type: 'boolean', + optional: true + }, + { + name: 'persistentPollingInterval', + description: 'Polling interval in milliseconds to get persistent RPC command response', + type: 'number', + optional: true + } + ], + return: { + description: 'A command execution Observable.', + type: 'Observable<any>' } - ], - return: { - description: 'A command execution Observable.', - type: 'Observable<any>' - } - }, - sendTwoWayCommand: { - description: 'Sends two way (with response) RPC command to the device.', - meta: 'function', - args: [ - { - name: 'method', - description: 'RPC method name', - type: 'string' - }, - { - name: 'params', - description: 'RPC method params, custom json object', - type: 'object', - optional: true - }, - { - name: 'timeout', - description: 'Maximum delay in milliseconds to wait until response/acknowledgement is received.', - type: 'number', - optional: true - }, - { - name: 'persistent', - description: 'RPC request persistent', - type: 'boolean', - optional: true - }, - { - name: 'persistentPollingInterval', - description: 'Polling interval in milliseconds to get persistent RPC command response', - type: 'number', - optional: true + }, + sendTwoWayCommand: { + description: 'Sends two way (with response) RPC command to the device.', + meta: 'function', + args: [ + { + name: 'method', + description: 'RPC method name', + type: 'string' + }, + { + name: 'params', + description: 'RPC method params, custom json object', + type: 'object', + optional: true + }, + { + name: 'timeout', + description: 'Maximum delay in milliseconds to wait until response/acknowledgement is received.', + type: 'number', + optional: true + }, + { + name: 'persistent', + description: 'RPC request persistent', + type: 'boolean', + optional: true + }, + { + name: 'persistentPollingInterval', + description: 'Polling interval in milliseconds to get persistent RPC command response', + type: 'number', + optional: true + } + ], + return: { + description: 'A command execution Observable of response body.', + type: 'Observable<any>' } - ], - return: { - description: 'A command execution Observable of response body.', - type: 'Observable<any>' } } - } - }, - actionsApi: { - description: 'Set of API functions to work with user defined actions.', - meta: 'property', - type: 'WidgetActionsApi', - children: { - getActionDescriptors: { - description: 'Get list of action descriptors for provided actionSourceId.', - meta: 'function', - args: [ - { - name: 'actionSourceId', - description: 'Id of widget action source', - type: 'string' + }, + actionsApi: { + description: 'Set of API functions to work with user defined actions.', + meta: 'property', + type: 'WidgetActionsApi', + children: { + getActionDescriptors: { + description: 'Get list of action descriptors for provided actionSourceId.', + meta: 'function', + args: [ + { + name: 'actionSourceId', + description: 'Id of widget action source', + type: 'string' + } + ], + return: { + description: 'The list of action descriptors', + type: 'Array<WidgetActionDescriptor>' } - ], - return: { - description: 'The list of action descriptors', - type: 'Array<WidgetActionDescriptor>' + }, + handleWidgetAction: { + description: 'Handle action produced by particular action source.', + meta: 'function', + args: [ + { + name: '$event', + description: 'DOM event object associated with action.', + type: 'Event' + }, + { + name: 'descriptor', + description: 'An action descriptor.', + type: 'WidgetActionDescriptor' + }, + { + name: 'entityId', + description: 'Current entity id provided by action source if available.', + type: entityIdHref, + optional: true + }, + { + name: 'entityName', + description: 'Current entity name provided by action source if available.', + type: 'string', + optional: true + } + ] } - }, - handleWidgetAction: { - description: 'Handle action produced by particular action source.', - meta: 'function', - args: [ - { - name: '$event', - description: 'DOM event object associated with action.', - type: 'Event' - }, - { - name: 'descriptor', - description: 'An action descriptor.', - type: 'WidgetActionDescriptor' - }, - { - name: 'entityId', - description: 'Current entity id provided by action source if available.', - type: entityIdHref, - optional: true - }, - { - name: 'entityName', - description: 'Current entity name provided by action source if available.', - type: 'string', - optional: true - } - ] } - } - }, - stateController: { - description: 'Reference to Dashboard state controller, providing API to manage current dashboard state.', - meta: 'property', - type: 'IStateController', - children: { - openState: { - description: 'Navigate to new dashboard state.', - meta: 'function', - args: [ - { - name: 'id', - description: 'An id of the target dashboard state.', + }, + stateController: { + description: 'Reference to Dashboard state controller, providing API to manage current dashboard state.', + meta: 'property', + type: 'IStateController', + children: { + openState: { + description: 'Navigate to new dashboard state.', + meta: 'function', + args: [ + { + name: 'id', + description: 'An id of the target dashboard state.', + type: 'string' + }, + { + name: 'params', + description: 'An object with state parameters to use by the new state.', + type: 'StateParams', + optional: true + }, + { + name: 'openRightLayout', + description: 'An optional boolean argument to force open right dashboard layout if present in mobile view mode.', + type: 'boolean', + optional: true + } + ] + }, + pushAndOpenState: { + description: 'Navigate to new dashboard state and adding intermediate states.', + meta: 'function', + args: [ + { + name: 'id', + description: 'An array state object of the target dashboard state.', + type: 'Array StateObject', + }, + { + name: 'openRightLayout', + description: 'An optional boolean argument to force open right dashboard layout if present in mobile view mode.', + type: 'boolean', + optional: true + } + ] + }, + updateState: { + description: 'Updates current dashboard state.', + meta: 'function', + args: [ + { + name: 'id', + description: 'An optional id of the target dashboard state to replace current state id.', + type: 'string', + optional: true + }, + { + name: 'params', + description: 'An object with state parameters to update current state parameters.', + type: 'StateParams', + optional: true + }, + { + name: 'openRightLayout', + description: 'An optional boolean argument to force open right dashboard layout if present in mobile view mode.', + type: 'boolean', + optional: true + } + ] + }, + getStateId: { + description: 'Get current dashboard state id.', + meta: 'function', + return: { + description: 'current dashboard state id.', type: 'string' - }, - { - name: 'params', - description: 'An object with state parameters to use by the new state.', - type: 'StateParams', - optional: true - }, - { - name: 'openRightLayout', - description: 'An optional boolean argument to force open right dashboard layout if present in mobile view mode.', - type: 'boolean', - optional: true } - ] - }, - pushAndOpenState: { - description: 'Navigate to new dashboard state and adding intermediate states.', - meta: 'function', - args: [ - { - name: 'id', - description: 'An array state object of the target dashboard state.', - type: 'Array StateObject', - }, - { - name: 'openRightLayout', - description: 'An optional boolean argument to force open right dashboard layout if present in mobile view mode.', - type: 'boolean', - optional: true + }, + getStateParams: { + description: 'Get current dashboard state parameters.', + meta: 'function', + return: { + description: 'current dashboard state parameters.', + type: 'StateParams' } - ] - }, - updateState: { - description: 'Updates current dashboard state.', - meta: 'function', - args: [ - { - name: 'id', - description: 'An optional id of the target dashboard state to replace current state id.', - type: 'string', - optional: true - }, - { - name: 'params', - description: 'An object with state parameters to update current state parameters.', - type: 'StateParams', - optional: true - }, - { - name: 'openRightLayout', - description: 'An optional boolean argument to force open right dashboard layout if present in mobile view mode.', - type: 'boolean', - optional: true + }, + getStateParamsByStateId: { + description: 'Get state parameters for particular dashboard state identified by id.', + meta: 'function', + args: [ + { + name: 'id', + description: 'An id of the target dashboard state.', + type: 'string' + } + ], + return: { + description: 'current dashboard state parameters.', + type: 'StateParams' } - ] - }, - getStateId: { - description: 'Get current dashboard state id.', - meta: 'function', - return: { - description: 'current dashboard state id.', - type: 'string' - } - }, - getStateParams: { - description: 'Get current dashboard state parameters.', - meta: 'function', - return: { - description: 'current dashboard state parameters.', - type: 'StateParams' - } - }, - getStateParamsByStateId: { - description: 'Get state parameters for particular dashboard state identified by id.', - meta: 'function', - args: [ - { - name: 'id', - description: 'An id of the target dashboard state.', - type: 'string' - } - ], - return: { - description: 'current dashboard state parameters.', - type: 'StateParams' } } } - } - }, - ...serviceCompletions + }, + ...serviceCompletions + } } } }; + +export const widgetContextCompletions: TbEditorCompletions = widgetContextCompletionsWithSettings(); diff --git a/ui-ngx/src/app/shared/models/dynamic-form.models.ts b/ui-ngx/src/app/shared/models/dynamic-form.models.ts new file mode 100644 index 0000000000..70c70b42b5 --- /dev/null +++ b/ui-ngx/src/app/shared/models/dynamic-form.models.ts @@ -0,0 +1,385 @@ +/// +/// 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 { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe'; +import { TbEditorCompletion, TbEditorCompletions } from '@shared/models/ace/completion.models'; +import { isString } from '@core/utils'; +import { JsonSchema, JsonSettingsSchema } from '@shared/models/widget.models'; +import JsonFormUtils from '@shared/components/json-form/react/json-form-utils'; +import { JsonFormData, KeyLabelItem } from '@shared/components/json-form/react/json-form.models'; + +export enum FormPropertyType { + text = 'text', + number = 'number', + switch = 'switch', + select = 'select', + color = 'color', + color_settings = 'color_settings', + font = 'font', + units = 'units', + icon = 'icon', + fieldset = 'fieldset' +} + +export const formPropertyTypes = Object.keys(FormPropertyType) as FormPropertyType[]; + +export const formPropertyTypeTranslations = new Map( + [ + [FormPropertyType.text, 'dynamic-form.property.type-text'], + [FormPropertyType.number, 'dynamic-form.property.type-number'], + [FormPropertyType.switch, 'dynamic-form.property.type-switch'], + [FormPropertyType.select, 'dynamic-form.property.type-select'], + [FormPropertyType.color, 'dynamic-form.property.type-color'], + [FormPropertyType.color_settings, 'dynamic-form.property.type-color-settings'], + [FormPropertyType.font, 'dynamic-form.property.type-font'], + [FormPropertyType.units, 'dynamic-form.property.type-units'], + [FormPropertyType.icon, 'dynamic-form.property.type-icon'], + [FormPropertyType.fieldset, 'dynamic-form.property.type-fieldset'] + ] +); + +export const formPropertyRowClasses = + ['column', 'column-xs', 'column-lt-md', 'align-start', 'no-border', 'no-gap', 'no-padding', 'same-padding']; + +export const formPropertyFieldClasses = + ['medium-width', 'flex', 'flex-xs', 'flex-lt-md']; + +export type PropertyConditionFunction = (property: FormProperty, model: any) => boolean; + +export interface FormPropertyBase { + id: string; + name: string; + group?: string; + type: FormPropertyType; + default: any; + required?: boolean; + subLabel?: string; + divider?: boolean; + fieldSuffix?: string; + disableOnProperty?: string; + condition?: string; + conditionFunction?: PropertyConditionFunction; + disabled?: boolean; + visible?: boolean; + rowClass?: string; + fieldClass?: string; +} + +export interface FormNumberProperty extends FormPropertyBase { + min?: number; + max?: number; + step?: number; +} + +export interface FormFieldSetProperty extends FormPropertyBase { + properties?: FormProperty[]; +} + +export interface FormSelectItem { + value: any; + label: string; +} + +export interface FormSelectProperty extends FormPropertyBase { + multiple?: boolean; + items?: FormSelectItem[]; +} + +export type FormProperty = FormPropertyBase & FormNumberProperty & FormSelectProperty & FormFieldSetProperty; + +export enum FormPropertyContainerType { + row = 'row', + fieldset = 'fieldset' +} + +export interface FormPropertyContainerBase { + type: FormPropertyContainerType; + label: string; + properties: FormProperty[]; + visible: boolean; +} + +export interface FormPropertyRow extends FormPropertyContainerBase { + switch?: FormProperty; + rowClass?: string; +} + +export interface FormPropertyFieldset extends FormPropertyContainerBase { + property?: FormProperty; +} + +export type FormPropertyContainer = FormPropertyRow & FormPropertyFieldset; + +export interface FormPropertyGroup { + title?: string; + containers: FormPropertyContainer[]; + visible: boolean; +} + +export const toPropertyGroups = (properties: FormProperty[]): FormPropertyGroup[] => { + const groups: {title: string, properties: FormProperty[]}[] = []; + for (let property of properties) { + if (!property.group) { + groups.push({ + title: null, + properties: [property] + }); + } else { + let propertyGroup = groups.find(g => g.title === property.group); + if (!propertyGroup) { + propertyGroup = { + title: property.group, + properties: [] + }; + groups.push(propertyGroup); + } + propertyGroup.properties.push(property); + } + } + return groups.map(g => ({ + title: g.title, + containers: toPropertyContainers(g.properties), + visible: true + })); +}; + +const toPropertyContainers = (properties: FormProperty[]): FormPropertyContainer[] => { + const result: FormPropertyContainer[] = []; + for (let property of properties) { + if (property.type === FormPropertyType.fieldset) { + const propertyFieldset: FormPropertyFieldset = { + property, + label: property.name, + type: FormPropertyContainerType.fieldset, + properties: property.properties, + visible: true + }; + result.push(propertyFieldset); + } else { + let propertyRow = + result.find(r => r.type === FormPropertyContainerType.row && r.label === property.name); + if (!propertyRow) { + propertyRow = { + label: property.name, + type: FormPropertyContainerType.row, + properties: [], + rowClass: property.rowClass, + visible: true + }; + result.push(propertyRow); + } + if (property.type === FormPropertyType.switch) { + propertyRow.switch = property; + } else { + propertyRow.properties.push(property); + } + } + } + return result; +} + +export const defaultFormProperties = (properties: FormProperty[]): {[id: string]: any} => { + const formProperties: {[id: string]: any} = {}; + for (const property of properties) { + formProperties[property.id] = defaultFormPropertyValue(property); + } + return formProperties; +}; + +export const defaultFormPropertyValue = (property: FormProperty): any => { + if (property.type === FormPropertyType.fieldset) { + const propertyValue: {[id: string]: any} = {}; + for (const childProperty of property.properties) { + propertyValue[childProperty.id] = defaultFormPropertyValue(childProperty); + } + return propertyValue; + } else { + return property.default; + } +} + +export const formPropertyCompletions = (properties: FormProperty[], customTranslate: CustomTranslatePipe): TbEditorCompletions => { + const propertiesCompletions: TbEditorCompletions = {}; + for (const property of properties) { + propertiesCompletions[property.id] = formPropertyCompletion(property, customTranslate); + } + return propertiesCompletions; +} + +export const formPropertyCompletion = (property: FormProperty, customTranslate: CustomTranslatePipe): TbEditorCompletion => { + let description = customTranslate.transform(property.name, property.name); + if (property.subLabel) { + description += ` ${customTranslate.transform(property.subLabel, property.subLabel)}`; + } + if (property.type === FormPropertyType.select) { + if (property.multiple) { + description += '

Possible values of array element:'; + } else { + description += '

Possible values:'; + } + description += `
    ${property.items.map(item => `
  • ${item.value} ${typeof item.value}
  • `).join('\n')}
`; + } + const completion: TbEditorCompletion = { + meta: 'property', + description, + type: formPropertyCompletionType(property) + }; + if (property.type === FormPropertyType.fieldset) { + completion.children = {}; + for (const childProperty of property.properties) { + completion.children[childProperty.id] = formPropertyCompletion(childProperty, customTranslate); + } + } + return completion; +}; + +const formPropertyCompletionType = (property: FormProperty): string => { + switch (property.type) { + case FormPropertyType.text: + return 'string'; + case FormPropertyType.number: + return 'number'; + case FormPropertyType.switch: + return 'boolean'; + case FormPropertyType.select: + const items = property.items || []; + const types: string[] = []; + items.forEach(item => { + const type = typeof item.value; + if (!types.includes(type)) { + types.push(type); + } + }); + const typesString = types.length ? types.join(' | ') : 'string'; + if (property.multiple) { + return `Array<${typesString}>`; + } else { + return typesString; + } + case FormPropertyType.color: + return 'color string'; + case FormPropertyType.color_settings: + return 'ColorProcessor'; + case FormPropertyType.font: + return 'Font'; + case FormPropertyType.units: + return 'units string'; + case FormPropertyType.icon: + return 'icon string'; + case FormPropertyType.fieldset: + return 'object'; + } +}; + + +export const jsonFormSchemaToFormProperties = (rawSchema: string | any) : FormProperty[] => { + const properties: FormProperty[] = []; + let settingsSchema: JsonSettingsSchema; + if (!rawSchema || rawSchema === '') { + settingsSchema = {}; + } else { + settingsSchema = isString(rawSchema) ? JSON.parse(rawSchema) : rawSchema; + } + if (settingsSchema.schema) { + const schema = settingsSchema.schema; + const form = settingsSchema.form || ['*']; + const groupInfoes = settingsSchema.groupInfoes || []; + if (form.length > 0) { + if (groupInfoes.length) { + for (const info of groupInfoes) { + const theForm: any[] = form[info.formIndex]; + properties.push(...schemaFormToProperties(schema, theForm, info.GroupTitle)); + } + } else { + properties.push(...schemaFormToProperties(schema, form)); + } + } + } + return properties; +} + +const schemaFormToProperties = (schema: JsonSchema, theForm: any[], groupTitle?: string): FormProperty[] => { + const merged = JsonFormUtils.merge(schema, theForm, {}, { + formDefaults: { + startEmpty: true + } + }); + return merged.map((form: JsonFormData) => jsonFormDataToProperty(form, 0, groupTitle)).filter(p => p != null); +} + +const jsonFormDataToProperty = (form: JsonFormData, level: number, groupTitle?: string): FormProperty => { + if (form.key && form.key.length > level) { + const property: FormProperty = { + id: form.key[level] + '', + name: form.title, + group: groupTitle, + type: null, + default: form.schema.default, + required: form.required + }; + if (form.condition?.length) { + property.condition = `return ${form.condition};`; + } + switch (form.type) { + case 'number': + property.type = FormPropertyType.number; + break; + case 'text': + property.type = FormPropertyType.text; + property.fieldClass = 'flex'; + break; + case 'checkbox': + property.type = FormPropertyType.switch; + break; + case 'rc-select': + property.type = FormPropertyType.select; + if (form.items?.length) { + property.items = (form.items as KeyLabelItem[]).map(item => ({value: item.value, label: item.label})); + } else { + property.items = []; + } + property.multiple = form.multiple; + property.fieldClass = 'flex'; + break; + case 'select': + property.type = FormPropertyType.select; + if (form.titleMap?.length) { + property.items = form.titleMap.map(item => ({value: item.value, label: item.name})); + } else { + property.items = []; + } + property.multiple = false; + property.fieldClass = 'flex'; + break; + case 'color': + property.type = FormPropertyType.color; + break; + case 'icon': + property.type = FormPropertyType.icon; + break; + case 'fieldset': + property.type = FormPropertyType.fieldset; + property.properties = form.items ? (form.items as JsonFormData[]).map(item => + jsonFormDataToProperty(item, level+1)).filter(p => p !== null) : []; + break; + } + if (!property.type) { + return null; + } + return property; + } + return null; +} diff --git a/ui-ngx/src/app/shared/models/widget.models.ts b/ui-ngx/src/app/shared/models/widget.models.ts index 37ea1c1607..6f5579c77f 100644 --- a/ui-ngx/src/app/shared/models/widget.models.ts +++ b/ui-ngx/src/app/shared/models/widget.models.ts @@ -45,6 +45,7 @@ import { HasTenantId, HasVersion } from '@shared/models/entity.models'; import { DataKeysCallbacks, DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models'; import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-config.component.models'; import { TbFunction } from '@shared/models/js-function.models'; +import { FormProperty } from '@shared/models/dynamic-form.models'; export enum widgetType { timeseries = 'timeseries', @@ -153,6 +154,7 @@ export interface WidgetTypeDescriptor { templateHtml: string; templateCss: string; controllerScript: TbFunction; + settingsForm?: FormProperty[]; settingsSchema?: string | any; dataKeySettingsSchema?: string | any; latestDataKeySettingsSchema?: string | any; @@ -830,6 +832,12 @@ export interface JsonSettingsSchema { groupInfoes?: GroupInfo[]; } +export interface DynamicFormData { + settingsForm?: FormProperty[]; + model?: any; + settingsDirective?: string; +} + export interface WidgetPosition { row: number; column: number; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index c2f45da84f..c43ff76da3 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1617,6 +1617,53 @@ "lwm2m-command": "Use the following documentation to connect the device through the LWM2M." } }, + "dynamic-form": { + "property": { + "properties": "Properties", + "property": "Property", + "id": "Id", + "name": "Name", + "type": "Type", + "type-text": "Text", + "type-number": "Number", + "type-switch": "Switch", + "type-select": "Select", + "type-color": "Color", + "type-color-settings": "Color settings", + "type-font": "Font", + "type-units": "Units", + "type-icon": "Icon", + "type-fieldset": "Fieldset", + "group-title": "Group title", + "no-properties": "No properties configured", + "add-property": "Add property", + "property-settings": "Property settings", + "remove-property": "Remove property", + "default-value": "Default value", + "value-required": "Value required", + "number-settings": "Number settings", + "min": "Min", + "max": "Max", + "step": "Step", + "advanced-ui-settings": "Advanced UI settings", + "disable-on-property": "Disable on property", + "display-condition-function": "Display condition function", + "sub-label": "Sub label", + "vertical-divider-after": "Vertical divider after", + "input-field-suffix": "Input field suffix", + "property-row-classes": "Property row classes", + "property-field-classes": "Property field classes", + "not-unique-property-ids-error": "Property Ids must be unique!", + "enable-multiple-select": "Enable multiple select", + "select-options": "Select options", + "not-unique-select-option-value-error": "Select option values must be unique!", + "value": "Value", + "label": "Label", + "add-option": "Add option", + "no-options": "No options configured", + "remove-option": "Remove option" + } + }, "asset-profile": { "asset-profile": "Asset profile", "asset-profiles": "Asset profiles", @@ -3099,38 +3146,6 @@ "not-unique-behavior-ids-error": "Behavior Ids must be unique!", "default-settings": "Default settings" }, - "property": { - "property": "Property", - "id": "Id", - "name": "Name", - "type": "Type", - "type-text": "Text", - "type-number": "Number", - "type-switch": "Switch", - "type-color": "Color", - "type-color-settings": "Color settings", - "type-font": "Font", - "type-units": "Units", - "type-icon": "Icon", - "no-properties": "No properties configured", - "add-property": "Add property", - "property-settings": "Property settings", - "remove-property": "Remove property", - "default-value": "Default value", - "value-required": "Value required", - "number-settings": "Number settings", - "min": "Min", - "max": "Max", - "step": "Step", - "advanced-ui-settings": "Advanced UI settings", - "disable-on-property": "Disable on property", - "sub-label": "Sub label", - "vertical-divider-after": "Vertical divider after", - "input-field-suffix": "Input field suffix", - "property-row-classes": "Property row classes", - "property-field-classes": "Property field classes", - "not-unique-property-ids-error": "Property Ids must be unique!" - }, "symbol": { "symbol": "SCADA symbol", "fluid-presence": "Fluid presence", @@ -5429,6 +5444,7 @@ "html": "HTML", "tidy": "Tidy", "css": "CSS", + "settings-form-properties": "Settings form properties", "settings-schema": "Settings schema", "datakey-settings-schema": "Data key settings schema", "latest-datakey-settings-schema": "Latest data key settings schema", From 6c76e0b2fe5bce16914a1995b36e1541c7e82dec Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 13 Dec 2024 12:38:56 +0200 Subject: [PATCH 006/108] TbMsg refactoring to use builders --- .../thingsboard/server/common/msg/TbMsg.java | 308 ++++++++++-------- 1 file changed, 175 insertions(+), 133 deletions(-) diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java index 47fb80c90e..46c31cf245 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java @@ -39,6 +39,8 @@ import java.io.Serializable; import java.util.Objects; import java.util.UUID; +import static java.util.Objects.requireNonNull; + /** * Created by ashvayka on 13.01.18. */ @@ -81,6 +83,79 @@ public final class TbMsg implements Serializable { return ctx.getAndIncrementRuleNodeCounter(); } + /** + * Transforms an existing TbMsg instance by changing its message type, originator, metadata, and data. + * + *

Deprecated: This method is deprecated since version 3.6.0 and should only be used when you need to + * specify a custom message type that doesn't exist in the {@link TbMsgType} enum. For standard message types, + * it is recommended to use the {@link #transformMsg(TbMsg, TbMsgType, EntityId, TbMsgMetaData, String)} + * method instead.

+ * + * + * @param tbMsg the TbMsg instance to transform + * @param type the new message type + * @param originator the new originator + * @param metaData the new metadata + * @param data the new data + * @return the transformed TbMsg instance + */ + @Deprecated(since = "3.6.0") + public static TbMsg transformMsg(TbMsg tbMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) { + return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, null, type, originator, tbMsg.customerId, metaData.copy(), tbMsg.dataType, + data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.callback); + } + + public static TbMsg transformMsg(TbMsg tbMsg, TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data) { + return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, type, type.name(), originator, tbMsg.customerId, metaData.copy(), tbMsg.dataType, + data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.callback); + } + + public static TbMsg transformMsgOriginator(TbMsg tbMsg, EntityId originatorId) { + return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, originatorId, tbMsg.getCustomerId(), tbMsg.metaData, tbMsg.dataType, + tbMsg.data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); + } + + public static TbMsg transformMsgData(TbMsg tbMsg, String data) { + return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, + data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); + } + + public static TbMsg transformMsgMetadata(TbMsg tbMsg, TbMsgMetaData metadata) { + return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, metadata.copy(), tbMsg.dataType, + tbMsg.data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); + } + + public static TbMsg transformMsg(TbMsg tbMsg, TbMsgMetaData metadata, String data) { + return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, metadata, tbMsg.dataType, + data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); + } + + public static TbMsg transformMsgCustomerId(TbMsg tbMsg, CustomerId customerId) { + return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, customerId, tbMsg.metaData, tbMsg.dataType, + tbMsg.data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); + } + + public static TbMsg transformMsgRuleChainId(TbMsg tbMsg, RuleChainId ruleChainId) { + return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, + tbMsg.data, ruleChainId, null, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); + } + + public static TbMsg transformMsgQueueName(TbMsg tbMsg, String queueName) { + return new TbMsg(queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, + tbMsg.data, tbMsg.getRuleChainId(), null, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); + } + + public static TbMsg transformMsg(TbMsg tbMsg, RuleChainId ruleChainId, String queueName) { + return new TbMsg(queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, + tbMsg.data, ruleChainId, null, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); + } + + //used for enqueueForTellNext + public static TbMsg newMsg(TbMsg tbMsg, String queueName, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { + return new TbMsg(queueName, UUID.randomUUID(), tbMsg.getTs(), tbMsg.getInternalType(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.customerId, tbMsg.getMetaData().copy(), + tbMsg.getDataType(), tbMsg.getData(), ruleChainId, ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), TbMsgCallback.EMPTY); + } + @Deprecated(since = "3.6.0", forRemoval = true) public static TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { return newMsg(queueName, type, originator, null, metaData, data, ruleChainId, ruleNodeId); @@ -106,8 +181,15 @@ public final class TbMsg implements Serializable { */ @Deprecated(since = "3.6.0") public static TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), null, type, originator, customerId, - metaData.copy(), TbMsgDataType.JSON, data, ruleChainId, ruleNodeId, null, TbMsgCallback.EMPTY); + return TbMsg.builder() + .queueName(queueName) + .type(type) + .originator(originator) + .metaData(metaData.copy()) + .data(data) + .ruleChainId(ruleChainId) + .ruleNodeId(ruleNodeId) + .build(); } @Deprecated(since = "3.6.0", forRemoval = true) @@ -117,8 +199,13 @@ public final class TbMsg implements Serializable { @Deprecated(since = "3.6.0", forRemoval = true) public static TbMsg newMsg(String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { - return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), null, type, originator, customerId, - metaData.copy(), TbMsgDataType.JSON, data, null, null, null, TbMsgCallback.EMPTY); + return TbMsg.builder() + .type(type) + .originator(originator) + .customerId(customerId) + .metaData(metaData.copy()) + .data(data) + .build(); } public static TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { @@ -126,8 +213,16 @@ public final class TbMsg implements Serializable { } public static TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId, - metaData.copy(), TbMsgDataType.JSON, data, ruleChainId, ruleNodeId, null, TbMsgCallback.EMPTY); + return TbMsg.builder() + .queueName(queueName) + .internalType(type) + .originator(originator) + .customerId(customerId) + .metaData(metaData.copy()) + .data(data) + .ruleChainId(ruleChainId) + .ruleNodeId(ruleNodeId) + .build(); } public static TbMsg newMsg(TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data) { @@ -135,13 +230,23 @@ public final class TbMsg implements Serializable { } public static TbMsg newMsg(TbMsgType type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { - return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId, - metaData.copy(), TbMsgDataType.JSON, data, null, null, null, TbMsgCallback.EMPTY); + return TbMsg.builder() + .internalType(type) + .originator(originator) + .customerId(customerId) + .metaData(metaData.copy()) + .data(data) + .build(); } public static TbMsg newMsg(TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data, long ts) { - return new TbMsg(null, UUID.randomUUID(), ts, type, originator, null, - metaData.copy(), TbMsgDataType.JSON, data, null, null, null, TbMsgCallback.EMPTY); + return TbMsg.builder() + .ts(ts) + .internalType(type) + .originator(originator) + .metaData(metaData.copy()) + .data(data) + .build(); } // REALLY NEW MSG @@ -184,14 +289,14 @@ public final class TbMsg implements Serializable { */ @Deprecated(since = "3.6.0") public static TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { - return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), null, type, originator, customerId, - metaData.copy(), TbMsgDataType.JSON, data, null, null, null, TbMsgCallback.EMPTY); - } - - @Deprecated(since = "3.6.0", forRemoval = true) - public static TbMsg newMsg(String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data) { - return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), null, type, originator, customerId, - metaData.copy(), dataType, data, null, null, null, TbMsgCallback.EMPTY); + return TbMsg.builder() + .queueName(queueName) + .type(type) + .originator(originator) + .customerId(customerId) + .metaData(metaData.copy()) + .data(data) + .build(); } /** @@ -211,7 +316,14 @@ public final class TbMsg implements Serializable { */ @Deprecated(since = "3.6.0") public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data) { - return newMsg(type, originator, null, metaData, dataType, data); + return TbMsg.builder() + .type(type) + .originator(originator) + .customerId(null) + .metaData(metaData.copy()) + .dataType(dataType) + .data(data) + .build(); } public static TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data) { @@ -219,138 +331,66 @@ public final class TbMsg implements Serializable { } public static TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { - return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId, - metaData.copy(), TbMsgDataType.JSON, data, null, null, null, TbMsgCallback.EMPTY); + return TbMsg.builder() + .queueName(queueName) + .internalType(type) + .originator(originator) + .customerId(customerId) + .metaData(metaData.copy()) + .data(data) + .build(); } public static TbMsg newMsg(TbMsgType type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data) { - return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId, - metaData.copy(), dataType, data, null, null, null, TbMsgCallback.EMPTY); + return TbMsg.builder() + .internalType(type) + .originator(originator) + .customerId(customerId) + .metaData(metaData.copy()) + .dataType(dataType) + .data(data) + .build(); } public static TbMsg newMsg(TbMsgType type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data) { return newMsg(type, originator, null, metaData, dataType, data); } - // For Tests only - - @Deprecated(since = "3.6.0", forRemoval = true) - public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), null, type, originator, null, - metaData.copy(), dataType, data, ruleChainId, ruleNodeId, null, TbMsgCallback.EMPTY); - } - - @Deprecated(since = "3.6.0", forRemoval = true) - public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data, TbMsgCallback callback) { - return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), null, type, originator, null, - metaData.copy(), TbMsgDataType.JSON, data, null, null, null, callback); - } - - /** - * Transforms an existing TbMsg instance by changing its message type, originator, metadata, and data. - * - *

Deprecated: This method is deprecated since version 3.6.0 and should only be used when you need to - * specify a custom message type that doesn't exist in the {@link TbMsgType} enum. For standard message types, - * it is recommended to use the {@link #transformMsg(TbMsg, TbMsgType, EntityId, TbMsgMetaData, String)} - * method instead.

- * - * - * @param tbMsg the TbMsg instance to transform - * @param type the new message type - * @param originator the new originator - * @param metaData the new metadata - * @param data the new data - * @return the transformed TbMsg instance - */ - @Deprecated(since = "3.6.0") - public static TbMsg transformMsg(TbMsg tbMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, null, type, originator, tbMsg.customerId, metaData.copy(), tbMsg.dataType, - data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.callback); - } - public static TbMsg newMsg(TbMsgType type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), type, originator, null, - metaData.copy(), dataType, data, ruleChainId, ruleNodeId, null, TbMsgCallback.EMPTY); + return TbMsg.builder() + .internalType(type) + .originator(originator) + .metaData(metaData.copy()) + .dataType(dataType) + .data(data) + .ruleChainId(ruleChainId) + .ruleNodeId(ruleNodeId) + .build(); } public static TbMsg newMsg(TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data, TbMsgCallback callback) { - return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), type, originator, null, - metaData.copy(), TbMsgDataType.JSON, data, null, null, null, callback); - } - - public static TbMsg transformMsg(TbMsg tbMsg, TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, type, type.name(), originator, tbMsg.customerId, metaData.copy(), tbMsg.dataType, - data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.callback); - } - - public static TbMsg transformMsgOriginator(TbMsg tbMsg, EntityId originatorId) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, originatorId, tbMsg.getCustomerId(), tbMsg.metaData, tbMsg.dataType, - tbMsg.data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); - } - - public static TbMsg transformMsgData(TbMsg tbMsg, String data) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, - data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); - } - - public static TbMsg transformMsgMetadata(TbMsg tbMsg, TbMsgMetaData metadata) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, metadata.copy(), tbMsg.dataType, - tbMsg.data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); - } - - public static TbMsg transformMsg(TbMsg tbMsg, TbMsgMetaData metadata, String data) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, metadata, tbMsg.dataType, - data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); - } - - public static TbMsg transformMsgCustomerId(TbMsg tbMsg, CustomerId customerId) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, customerId, tbMsg.metaData, tbMsg.dataType, - tbMsg.data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); - } - - public static TbMsg transformMsgRuleChainId(TbMsg tbMsg, RuleChainId ruleChainId) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, - tbMsg.data, ruleChainId, null, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); - } - - public static TbMsg transformMsgQueueName(TbMsg tbMsg, String queueName) { - return new TbMsg(queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, - tbMsg.data, tbMsg.getRuleChainId(), null, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); - } - - public static TbMsg transformMsg(TbMsg tbMsg, RuleChainId ruleChainId, String queueName) { - return new TbMsg(queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, - tbMsg.data, ruleChainId, null, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); - } - - //used for enqueueForTellNext - public static TbMsg newMsg(TbMsg tbMsg, String queueName, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return new TbMsg(queueName, UUID.randomUUID(), tbMsg.getTs(), tbMsg.getInternalType(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.customerId, tbMsg.getMetaData().copy(), - tbMsg.getDataType(), tbMsg.getData(), ruleChainId, ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), TbMsgCallback.EMPTY); - } - - private TbMsg(String queueName, UUID id, long ts, TbMsgType internalType, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data, - RuleChainId ruleChainId, RuleNodeId ruleNodeId, TbMsgProcessingCtx ctx, TbMsgCallback callback) { - this(queueName, id, ts, internalType, internalType.name(), originator, customerId, metaData, dataType, data, ruleChainId, ruleNodeId, ctx, callback); - } - - private TbMsg(String queueName, UUID id, long ts, TbMsgType internalType, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data, - RuleChainId ruleChainId, RuleNodeId ruleNodeId, TbMsgProcessingCtx ctx, TbMsgCallback callback) { - this(queueName, id, ts, internalType, type, originator, customerId, metaData, dataType, data, ruleChainId, ruleNodeId, null, null, ctx, callback); + return TbMsg.builder() + .internalType(type) + .originator(originator) + .metaData(metaData.copy()) + .data(data) + .callback(callback) + .build(); } + @Builder private TbMsg(String queueName, UUID id, long ts, TbMsgType internalType, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId, UUID correlationId, Integer partition, TbMsgProcessingCtx ctx, TbMsgCallback callback) { - this.id = id; + this.id = id != null ? id : UUID.randomUUID(); this.queueName = queueName; if (ts > 0) { this.ts = ts; } else { this.ts = System.currentTimeMillis(); } - this.type = type; this.internalType = internalType != null ? internalType : getInternalType(type); - this.originator = originator; + this.type = type != null ? type : this.internalType.name(); + this.originator = requireNonNull(originator, "msg originator is missing"); if (customerId == null || customerId.isNullUid()) { if (originator != null && originator.getEntityType() == EntityType.CUSTOMER) { this.customerId = new CustomerId(originator.getId()); @@ -361,8 +401,8 @@ public final class TbMsg implements Serializable { this.customerId = customerId; } this.metaData = metaData; - this.dataType = dataType; - this.data = data; + this.dataType = dataType != null ? dataType : TbMsgDataType.JSON; + this.data = requireNonNull(data, "msg data is missing"); this.ruleChainId = ruleChainId; this.ruleNodeId = ruleNodeId; this.correlationId = correlationId; @@ -510,11 +550,13 @@ public final class TbMsg implements Serializable { } private TbMsgType getInternalType(String type) { - try { - return TbMsgType.valueOf(type); - } catch (IllegalArgumentException e) { - return TbMsgType.NA; + if (type != null) { + try { + return TbMsgType.valueOf(type); + } catch (IllegalArgumentException ignored) { + } } + return TbMsgType.NA; } public boolean isTypeOf(TbMsgType tbMsgType) { From cbd2e012ddff8277574a9194e8d748f26f6dca0e Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 13 Dec 2024 13:05:54 +0200 Subject: [PATCH 007/108] Refactor TbMsg.newMsg usages --- .../actors/ruleChain/DefaultTbContext.java | 47 +- .../server/controller/RpcV2Controller.java | 7 +- .../controller/RuleChainController.java | 8 +- .../controller/RuleEngineController.java | 9 +- .../service/action/EntityActionService.java | 9 +- .../device/DeviceProvisionServiceImpl.java | 16 +- .../service/edge/rpc/EdgeGrpcService.java | 8 +- .../edge/rpc/processor/BaseEdgeProcessor.java | 9 +- .../processor/device/DeviceEdgeProcessor.java | 9 +- .../telemetry/BaseTelemetryProcessor.java | 34 +- .../entitiy/EntityStateSourcingListener.java | 10 +- .../rpc/DefaultTbCoreDeviceRpcService.java | 9 +- .../server/service/rpc/TbRpcService.java | 7 +- .../state/DefaultDeviceStateService.java | 9 +- .../transport/DefaultTransportApiService.java | 9 +- .../actors/rule/DefaultTbContextTest.java | 23 +- .../controller/RuleEngineControllerTest.java | 37 +- .../controller/TenantControllerTest.java | 7 +- ...AbstractRuleEngineFlowIntegrationTest.java | 16 +- ...actRuleEngineLifecycleIntegrationTest.java | 8 +- .../queue/DefaultTbClusterServiceTest.java | 38 +- .../TbRuleEngineQueueConsumerManagerTest.java | 7 +- .../DefaultTbRuleEngineRpcServiceTest.java | 7 +- .../DefaultRuleEngineCallServiceTest.java | 16 +- .../SequentialTimeseriesPersistenceTest.java | 12 +- .../thingsboard/server/common/msg/TbMsg.java | 373 ++++++---------- .../service/DefaultTransportService.java | 12 +- .../rule/engine/api/TbContext.java | 3 - .../rule/engine/api/util/TbNodeUtilsTest.java | 35 +- .../rule/engine/action/TbMsgCountNode.java | 9 +- .../deduplication/TbMsgDeduplicationNode.java | 29 +- .../rule/engine/delay/TbMsgDelayNode.java | 16 +- .../engine/profile/TbDeviceProfileNode.java | 24 +- .../action/TbAssignToCustomerNodeTest.java | 7 +- .../engine/action/TbClearAlarmNodeTest.java | 14 +- .../TbCopyAttributesToEntityViewNodeTest.java | 52 ++- .../engine/action/TbCreateAlarmNodeTest.java | 49 +- .../action/TbCreateRelationNodeTest.java | 14 +- .../action/TbDeleteRelationNodeTest.java | 7 +- .../engine/action/TbDeviceStateNodeTest.java | 22 +- .../rule/engine/action/TbLogNodeTest.java | 21 +- .../engine/action/TbMsgCountNodeTest.java | 21 +- .../TbSaveToCustomCassandraTableNodeTest.java | 35 +- .../TbUnassignFromCustomerNodeTest.java | 7 +- .../aws/lambda/TbAwsLambdaNodeTest.java | 35 +- .../rule/engine/aws/sns/TbSnsNodeTest.java | 14 +- .../rule/engine/aws/sqs/TbSqsNodeTest.java | 28 +- .../engine/debug/TbMsgGeneratorNodeTest.java | 28 +- .../engine/edge/TbMsgPushToEdgeNodeTest.java | 33 +- .../filter/TbAssetTypeSwitchNodeTest.java | 8 +- .../filter/TbCheckAlarmStatusNodeTest.java | 7 +- .../engine/filter/TbCheckMessageNodeTest.java | 14 +- .../filter/TbCheckRelationNodeTest.java | 7 +- .../filter/TbDeviceTypeSwitchNodeTest.java | 8 +- .../engine/filter/TbJsFilterNodeTest.java | 30 +- .../engine/filter/TbJsSwitchNodeTest.java | 10 +- .../filter/TbMsgTypeFilterNodeTest.java | 7 +- .../filter/TbMsgTypeSwitchNodeTest.java | 7 +- .../TbOriginatorTypeFilterNodeTest.java | 7 +- .../TbOriginatorTypeSwitchNodeTest.java | 7 +- .../rule/engine/flow/TbAckNodeTest.java | 7 +- .../engine/flow/TbCheckpointNodeTest.java | 14 +- .../engine/flow/TbRuleChainInputNodeTest.java | 7 +- .../flow/TbRuleChainOutputNodeTest.java | 7 +- .../engine/gcp/pubsub/TbPubSubNodeTest.java | 28 +- .../geo/TbGpsGeofencingActionNodeTest.java | 7 +- .../geo/TbGpsGeofencingFilterNodeTest.java | 14 +- .../rule/engine/kafka/TbKafkaNodeTest.java | 42 +- .../engine/mail/TbMsgToEmailNodeTest.java | 7 +- .../rule/engine/math/TbMathNodeTest.java | 133 +++++- .../metadata/CalculateDeltaNodeTest.java | 112 ++++- .../TbFetchDeviceCredentialsNodeTest.java | 8 +- .../metadata/TbGetAttributesNodeTest.java | 15 +- .../TbGetCustomerAttributeNodeTest.java | 21 +- .../TbGetCustomerDetailsNodeTest.java | 14 +- .../metadata/TbGetDeviceAttrNodeTest.java | 7 +- .../TbGetOriginatorFieldsNodeTest.java | 42 +- .../TbGetRelatedAttributeNodeTest.java | 14 +- .../metadata/TbGetTelemetryNodeTest.java | 70 ++- .../TbGetTenantAttributeNodeTest.java | 14 +- .../metadata/TbGetTenantDetailsNodeTest.java | 14 +- .../rule/engine/mqtt/TbMqttNodeTest.java | 14 +- .../rule/engine/profile/DeviceStateTest.java | 53 ++- .../profile/TbDeviceProfileNodeTest.java | 417 +++++++++++++++--- .../engine/rabbitmq/TbRabbitMqNodeTest.java | 21 +- .../rule/engine/rest/TbHttpClientTest.java | 17 +- .../engine/rest/TbRestApiCallNodeTest.java | 20 +- .../rest/TbSendRestApiCallReplyNodeTest.java | 14 +- .../engine/rpc/TbSendRPCReplyNodeTest.java | 36 +- .../engine/rpc/TbSendRPCRequestNodeTest.java | 140 +++++- .../telemetry/TbMsgAttributesNodeTest.java | 7 +- .../TbMsgDeleteAttributesNodeTest.java | 8 +- .../telemetry/TbMsgTimeseriesNodeTest.java | 28 +- .../transform/TbChangeOriginatorNodeTest.java | 42 +- .../engine/transform/TbCopyKeysNodeTest.java | 8 +- .../transform/TbDeleteKeysNodeTest.java | 8 +- .../engine/transform/TbJsonPathNodeTest.java | 8 +- .../transform/TbMsgDeduplicationNodeTest.java | 20 +- .../transform/TbRenameKeysNodeTest.java | 8 +- .../transform/TbSplitArrayMsgNodeTest.java | 8 +- .../transform/TbTransformMsgNodeTest.java | 30 +- 101 files changed, 2243 insertions(+), 632 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index 0070eba4a1..e456f9dd46 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -361,14 +361,18 @@ public class DefaultTbContext implements TbContext { nodeCtx.setSelf(self); } - @Override - public TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data) { - return newMsg(queueName, type, originator, null, metaData, data); - } - @Override public TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { - return TbMsg.newMsg(queueName, type, originator, customerId, metaData, data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId()); + return TbMsg.builder() + .queueName(queueName) + .type(type) + .originator(originator) + .customerId(customerId) + .metaData(metaData.copy()) + .data(data) + .ruleChainId(nodeCtx.getSelf().getRuleChainId()) + .ruleNodeId(nodeCtx.getSelf().getId()) + .build(); } @Override @@ -383,7 +387,16 @@ public class DefaultTbContext implements TbContext { @Override public TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { - return TbMsg.newMsg(queueName, type, originator, customerId, metaData, data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId()); + return TbMsg.builder() + .queueName(queueName) + .type(type) + .originator(originator) + .customerId(customerId) + .metaData(metaData.copy()) + .data(data) + .ruleChainId(nodeCtx.getSelf().getRuleChainId()) + .ruleNodeId(nodeCtx.getSelf().getId()) + .build(); } @Override @@ -497,7 +510,15 @@ public class DefaultTbContext implements TbContext { defaultQueueName = profile.getDefaultQueueName(); defaultRuleChainId = profile.getDefaultRuleChainId(); } - return TbMsg.newMsg(defaultQueueName, action, id, msgMetaData, msgData, defaultRuleChainId, null); + return TbMsg.builder() + .queueName(defaultQueueName) + .type(action) + .originator(id) + .metaData(msgMetaData.copy()) + .data(msgData) + .ruleChainId(defaultRuleChainId) + .ruleNodeId(null) + .build(); } public TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, TbMsgType actionMsgType, K profile) { @@ -515,7 +536,15 @@ public class DefaultTbContext implements TbContext { defaultQueueName = profile.getDefaultQueueName(); defaultRuleChainId = profile.getDefaultRuleChainId(); } - return TbMsg.newMsg(defaultQueueName, actionMsgType, id, msgMetaData, msgData, defaultRuleChainId, null); + return TbMsg.builder() + .queueName(defaultQueueName) + .type(actionMsgType) + .originator(id) + .metaData(msgMetaData.copy()) + .data(msgData) + .ruleChainId(defaultRuleChainId) + .ruleNodeId(null) + .build(); } @Override diff --git a/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java b/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java index 67ff1b3fc5..51a13321e7 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java @@ -239,7 +239,12 @@ public class RpcV2Controller extends AbstractRpcController { rpcService.deleteRpc(getTenantId(), rpcId); rpc.setStatus(RpcStatus.DELETED); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_DELETED, rpc.getDeviceId(), TbMsgMetaData.EMPTY, JacksonUtil.toString(rpc)); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.RPC_DELETED) + .originator(rpc.getDeviceId()) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(JacksonUtil.toString(rpc)) + .build(); tbClusterService.pushMsgToRuleEngine(getTenantId(), rpc.getDeviceId(), msg, null); } } diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java index 63a435e6f1..ba7e8e87bc 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -384,7 +384,13 @@ public class RuleChainController extends BaseController { } engine = new RuleNodeTbelScriptEngine(getTenantId(), tbelInvokeService, script, argNames); } - TbMsg inMsg = TbMsg.newMsg(msgType, null, new TbMsgMetaData(metadata), TbMsgDataType.JSON, data); + TbMsg inMsg = TbMsg.builder() + .type(msgType) + .originator(null) + .metaData(new TbMsgMetaData(metadata).copy()) + .dataType(TbMsgDataType.JSON) + .data(data) + .build(); switch (scriptType) { case "update": output = msgToOutput(engine.executeUpdateAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS)); diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleEngineController.java b/application/src/main/java/org/thingsboard/server/controller/RuleEngineController.java index 8cda3d2212..6484553f80 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleEngineController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleEngineController.java @@ -169,7 +169,14 @@ public class RuleEngineController extends BaseController { metaData.put("serviceId", serviceInfoProvider.getServiceId()); metaData.put("requestUUID", requestId.toString()); metaData.put("expirationTime", Long.toString(expTime)); - TbMsg msg = TbMsg.newMsg(queueName, TbMsgType.REST_API_REQUEST, entityId, currentUser.getCustomerId(), new TbMsgMetaData(metaData), requestBody); + TbMsg msg = TbMsg.builder() + .queueName(queueName) + .type(TbMsgType.REST_API_REQUEST) + .originator(entityId) + .customerId(currentUser.getCustomerId()) + .metaData(new TbMsgMetaData(metaData).copy()) + .data(requestBody) + .build(); ruleEngineCallService.processRestApiCallToRuleEngine(currentUser.getTenantId(), requestId, msg, queueName != null, reply -> reply(new LocalRequestMetaData(msg, currentUser, result), reply)); } diff --git a/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java b/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java index ab6d32fb44..e743fc7a4e 100644 --- a/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java +++ b/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java @@ -172,7 +172,14 @@ public class EntityActionService { if (tenantId != null && !tenantId.isSysTenantId()) { processNotificationRules(tenantId, entityId, entity, actionType, user, additionalInfo); } - TbMsg tbMsg = TbMsg.newMsg(msgType.get(), entityId, customerId, metaData, TbMsgDataType.JSON, JacksonUtil.toString(entityNode)); + TbMsg tbMsg = TbMsg.builder() + .type(msgType.get()) + .originator(entityId) + .customerId(customerId) + .metaData(metaData.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(entityNode)) + .build(); tbClusterService.pushMsgToRuleEngine(tenantId, entityId, tbMsg, null); } catch (Exception e) { log.warn("[{}] Failed to push entity action to rule engine: {}", entityId, actionType, e); diff --git a/application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java b/application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java index f5b6411bf7..59f835b0a1 100644 --- a/application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java +++ b/application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java @@ -253,7 +253,13 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { private void pushProvisionEventToRuleEngine(ProvisionRequest request, Device device, TbMsgType type) { try { JsonNode entityNode = JacksonUtil.valueToTree(request); - TbMsg msg = TbMsg.newMsg(type, device.getId(), device.getCustomerId(), createTbMsgMetaData(device), JacksonUtil.toString(entityNode)); + TbMsg msg = TbMsg.builder() + .type(type) + .originator(device.getId()) + .customerId(device.getCustomerId()) + .metaData(createTbMsgMetaData(device).copy()) + .data(JacksonUtil.toString(entityNode)) + .build(); sendToRuleEngine(device.getTenantId(), msg, null); } catch (IllegalArgumentException e) { log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), type, e); @@ -263,7 +269,13 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { private void pushDeviceCreatedEventToRuleEngine(Device device) { try { ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(device); - TbMsg msg = TbMsg.newMsg(TbMsgType.ENTITY_CREATED, device.getId(), device.getCustomerId(), createTbMsgMetaData(device), JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode)); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.ENTITY_CREATED) + .originator(device.getId()) + .customerId(device.getCustomerId()) + .metaData(createTbMsgMetaData(device).copy()) + .data(JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode)) + .build(); sendToRuleEngine(device.getTenantId(), msg, null); } catch (JsonProcessingException | IllegalArgumentException e) { log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), TbMsgType.ENTITY_CREATED.name(), e); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java index 71387aa542..bef7c90632 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java @@ -575,7 +575,13 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i md.putValue("edgeName", edge.getName()); md.putValue("edgeType", edge.getType()); } - TbMsg tbMsg = TbMsg.newMsg(msgType, edgeId, md, TbMsgDataType.JSON, data); + TbMsg tbMsg = TbMsg.builder() + .type(msgType) + .originator(edgeId) + .metaData(md.copy()) + .dataType(TbMsgDataType.JSON) + .data(data) + .build(); clusterService.pushMsgToRuleEngine(tenantId, edgeId, tbMsg, null); } catch (Exception e) { log.warn("[{}][{}] Failed to push {}", tenantId, edge.getId(), msgType, e); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java index e448fbd937..f7981f81db 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java @@ -343,7 +343,14 @@ public abstract class BaseEdgeProcessor { protected void pushEntityEventToRuleEngine(TenantId tenantId, EntityId entityId, CustomerId customerId, TbMsgType msgType, String msgData, TbMsgMetaData metaData) { - TbMsg tbMsg = TbMsg.newMsg(msgType, entityId, customerId, metaData, TbMsgDataType.JSON, msgData); + TbMsg tbMsg = TbMsg.builder() + .type(msgType) + .originator(entityId) + .customerId(customerId) + .metaData(metaData.copy()) + .dataType(TbMsgDataType.JSON) + .data(msgData) + .build(); edgeCtx.getClusterService().pushMsgToRuleEngine(tenantId, entityId, tbMsg, new TbQueueCallback() { @Override public void onSuccess(TbQueueMsgMetadata metadata) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java index e453cbc26f..eae19082c5 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java @@ -190,8 +190,13 @@ public abstract class DeviceEdgeProcessor extends BaseDeviceProcessor implements ObjectNode data = JacksonUtil.newObjectNode(); data.put("method", deviceRpcCallMsg.getRequestMsg().getMethod()); data.put("params", deviceRpcCallMsg.getRequestMsg().getParams()); - TbMsg tbMsg = TbMsg.newMsg(TbMsgType.TO_SERVER_RPC_REQUEST, deviceId, null, metaData, - TbMsgDataType.JSON, JacksonUtil.toString(data)); + TbMsg tbMsg = TbMsg.builder() + .type(TbMsgType.TO_SERVER_RPC_REQUEST) + .originator(deviceId) + .metaData(metaData.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); edgeCtx.getClusterService().pushMsgToRuleEngine(tenantId, deviceId, tbMsg, new TbQueueCallback() { @Override public void onSuccess(TbQueueMsgMetadata metadata) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java index d94d4d8939..bc425bd624 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java @@ -208,7 +208,16 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList()); metaData.putValue("ts", tsKv.getTs() + ""); var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); - TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), TbMsgType.POST_TELEMETRY_REQUEST, entityId, customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null); + TbMsg tbMsg = TbMsg.builder() + .queueName(defaultQueueAndRuleChain.getKey()) + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(entityId) + .customerId(customerId) + .metaData(metaData.copy()) + .data(gson.toJson(json)) + .ruleChainId(defaultQueueAndRuleChain.getValue()) + .ruleNodeId(null) + .build(); edgeCtx.getClusterService().pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { @Override public void onSuccess(TbQueueMsgMetadata metadata) { @@ -252,7 +261,16 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { SettableFuture futureToSet = SettableFuture.create(); JsonObject json = JsonUtils.getJsonObject(msg.getKvList()); var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); - TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null); + TbMsg tbMsg = TbMsg.builder() + .queueName(defaultQueueAndRuleChain.getKey()) + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .customerId(customerId) + .metaData(metaData.copy()) + .data(gson.toJson(json)) + .ruleChainId(defaultQueueAndRuleChain.getValue()) + .ruleNodeId(null) + .build(); edgeCtx.getClusterService().pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { @Override public void onSuccess(TbQueueMsgMetadata metadata) { @@ -281,8 +299,16 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { @Override public void onSuccess(@Nullable Void tmp) { var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); - TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), TbMsgType.ATTRIBUTES_UPDATED, entityId, - customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null); + TbMsg tbMsg = TbMsg.builder() + .queueName(defaultQueueAndRuleChain.getKey()) + .type(TbMsgType.ATTRIBUTES_UPDATED) + .originator(entityId) + .customerId(customerId) + .metaData(metaData.copy()) + .data(gson.toJson(json)) + .ruleChainId(defaultQueueAndRuleChain.getValue()) + .ruleNodeId(null) + .build(); edgeCtx.getClusterService().pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { @Override public void onSuccess(TbQueueMsgMetadata metadata) { diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java b/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java index bce599c0e3..2fa2bfd0e2 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java @@ -250,8 +250,14 @@ public class EntityStateSourcingListener { private void pushAssignedFromNotification(Tenant currentTenant, TenantId newTenantId, Device assignedDevice) { String data = JacksonUtil.toString(JacksonUtil.valueToTree(assignedDevice)); if (data != null) { - TbMsg tbMsg = TbMsg.newMsg(TbMsgType.ENTITY_ASSIGNED_FROM_TENANT, assignedDevice.getId(), - assignedDevice.getCustomerId(), getMetaDataForAssignedFrom(currentTenant), TbMsgDataType.JSON, data); + TbMsg tbMsg = TbMsg.builder() + .type(TbMsgType.ENTITY_ASSIGNED_FROM_TENANT) + .originator(assignedDevice.getId()) + .customerId(assignedDevice.getCustomerId()) + .metaData(getMetaDataForAssignedFrom(currentTenant).copy()) + .dataType(TbMsgDataType.JSON) + .data(data) + .build(); tbClusterService.pushMsgToRuleEngine(newTenantId, assignedDevice.getId(), tbMsg, null); } } diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java index e8cce43aa2..0e65e55243 100644 --- a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java @@ -183,7 +183,14 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService { entityNode.put(DataConstants.ADDITIONAL_INFO, msg.getAdditionalInfo()); try { - TbMsg tbMsg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, msg.getDeviceId(), Optional.ofNullable(currentUser).map(User::getCustomerId).orElse(null), metaData, TbMsgDataType.JSON, JacksonUtil.toString(entityNode)); + TbMsg tbMsg = TbMsg.builder() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(msg.getDeviceId()) + .customerId(Optional.ofNullable(currentUser).map(User::getCustomerId).orElse(null)) + .metaData(metaData.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(entityNode)) + .build(); clusterService.pushMsgToRuleEngine(msg.getTenantId(), msg.getDeviceId(), tbMsg, null); } catch (IllegalArgumentException e) { throw new RuntimeException(e); diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java b/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java index 133818592e..abe4679afd 100644 --- a/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java @@ -63,7 +63,12 @@ public class TbRpcService { } private void pushRpcMsgToRuleEngine(TenantId tenantId, Rpc rpc) { - TbMsg msg = TbMsg.newMsg(TbMsgType.valueOf("RPC_" + rpc.getStatus().name()), rpc.getDeviceId(), TbMsgMetaData.EMPTY, JacksonUtil.toString(rpc)); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.valueOf("RPC_" + rpc.getStatus().name())) + .originator(rpc.getDeviceId()) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(JacksonUtil.toString(rpc)) + .build(); tbClusterService.pushMsgToRuleEngine(tenantId, rpc.getDeviceId(), msg, null); } diff --git a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java index 6025e874b9..18315c0372 100644 --- a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java @@ -857,7 +857,14 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService() { @@ -86,7 +91,12 @@ public class RuleEngineControllerTest extends AbstractControllerTest { loginTenantAdmin(); Device device = createDevice("Test", "123"); DeviceId deviceId = device.getId(); - TbMsg responseMsg = TbMsg.newMsg(TbMsgType.REST_API_REQUEST, deviceId, TbMsgMetaData.EMPTY, RESPONSE_BODY); + TbMsg responseMsg = TbMsg.builder() + .type(TbMsgType.REST_API_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(RESPONSE_BODY) + .build(); mockRestApiCallToRuleEngine(responseMsg); JsonNode apiResponse = doPostAsyncWithTypedResponse("/api/rule-engine/DEVICE/" + deviceId.getId(), REQUEST_BODY, new TypeReference<>() { @@ -110,7 +120,12 @@ public class RuleEngineControllerTest extends AbstractControllerTest { loginTenantAdmin(); Device device = createDevice("Test", "123"); DeviceId deviceId = device.getId(); - TbMsg responseMsg = TbMsg.newMsg(TbMsgType.REST_API_REQUEST, deviceId, TbMsgMetaData.EMPTY, RESPONSE_BODY); + TbMsg responseMsg = TbMsg.builder() + .type(TbMsgType.REST_API_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(RESPONSE_BODY) + .build(); mockRestApiCallToRuleEngine(responseMsg); JsonNode apiResponse = doPostAsyncWithTypedResponse("/api/rule-engine/DEVICE/" + deviceId.getId() + "/15000", REQUEST_BODY, new TypeReference<>() { @@ -156,7 +171,13 @@ public class RuleEngineControllerTest extends AbstractControllerTest { loginTenantAdmin(); Device device = createDevice("Test", "123"); DeviceId deviceId = device.getId(); - TbMsg responseMsg = TbMsg.newMsg(DataConstants.HP_QUEUE_NAME, TbMsgType.REST_API_REQUEST, deviceId, TbMsgMetaData.EMPTY, RESPONSE_BODY); + TbMsg responseMsg = TbMsg.builder() + .queueName(DataConstants.HP_QUEUE_NAME) + .type(TbMsgType.REST_API_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(RESPONSE_BODY) + .build(); mockRestApiCallToRuleEngine(responseMsg); JsonNode apiResponse = doPostAsyncWithTypedResponse("/api/rule-engine/DEVICE/" + deviceId.getId() + "/HighPriority/1000", REQUEST_BODY, new TypeReference<>() { @@ -195,7 +216,13 @@ public class RuleEngineControllerTest extends AbstractControllerTest { assignDeviceToCustomer(deviceId, customerId); loginCustomerUser(); - TbMsg responseMsg = TbMsg.newMsg(TbMsgType.REST_API_REQUEST, deviceId, customerId, TbMsgMetaData.EMPTY, RESPONSE_BODY); + TbMsg responseMsg = TbMsg.builder() + .type(TbMsgType.REST_API_REQUEST) + .originator(deviceId) + .customerId(customerId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(RESPONSE_BODY) + .build(); mockRestApiCallToRuleEngine(responseMsg); JsonNode apiResponse = doPostAsyncWithTypedResponse("/api/rule-engine/DEVICE/" + deviceId.getId(), REQUEST_BODY, new TypeReference<>() { diff --git a/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java index c8cc72c5b5..f2f843e448 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java @@ -743,7 +743,12 @@ public class TenantControllerTest extends AbstractControllerTest { } private TbMsg publishTbMsg(TenantId tenantId, TopicPartitionInfo tpi) { - TbMsg tbMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, tenantId, TbMsgMetaData.EMPTY, "{\"test\":1}"); + TbMsg tbMsg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(tenantId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data("{\"test\":1}") + .build(); TransportProtos.ToRuleEngineMsg msg = TransportProtos.ToRuleEngineMsg.newBuilder() .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) diff --git a/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java b/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java index fba5f2187f..c47c847241 100644 --- a/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java @@ -184,7 +184,13 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule TbMsgCallback tbMsgCallback = Mockito.mock(TbMsgCallback.class); Mockito.when(tbMsgCallback.isMsgValid()).thenReturn(true); - TbMsg tbMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, device.getId(), TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT, tbMsgCallback); + TbMsg tbMsg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(device.getId()) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .callback(tbMsgCallback) + .build(); QueueToRuleEngineMsg qMsg = new QueueToRuleEngineMsg(savedTenant.getId(), tbMsg, null, null); // Pushing Message to the system actorSystem.tell(qMsg); @@ -309,7 +315,13 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule TbMsgCallback tbMsgCallback = Mockito.mock(TbMsgCallback.class); Mockito.when(tbMsgCallback.isMsgValid()).thenReturn(true); - TbMsg tbMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, device.getId(), TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT, tbMsgCallback); + TbMsg tbMsg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(device.getId()) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .callback(tbMsgCallback) + .build(); QueueToRuleEngineMsg qMsg = new QueueToRuleEngineMsg(savedTenant.getId(), tbMsg, null, null); // Pushing Message to the system actorSystem.tell(qMsg); diff --git a/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java b/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java index d8bd02ec7f..780bd3c8cb 100644 --- a/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java @@ -142,7 +142,13 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac log.warn("attr updated"); TbMsgCallback tbMsgCallback = Mockito.mock(TbMsgCallback.class); Mockito.when(tbMsgCallback.isMsgValid()).thenReturn(true); - TbMsg tbMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, device.getId(), TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT, tbMsgCallback); + TbMsg tbMsg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(device.getId()) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .callback(tbMsgCallback) + .build(); QueueToRuleEngineMsg qMsg = new QueueToRuleEngineMsg(tenantId, tbMsg, null, null); // Pushing Message to the system log.warn("before tell tbMsgCallback"); diff --git a/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbClusterServiceTest.java b/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbClusterServiceTest.java index 25fe589a08..ad9e6d8d87 100644 --- a/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbClusterServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbClusterServiceTest.java @@ -291,7 +291,13 @@ public class DefaultTbClusterServiceTest { TbQueueCallback callback = mock(TbQueueCallback.class); TenantId tenantId = TenantId.fromUUID(UUID.fromString("3c8bd350-1239-4a3b-b9c3-4dd76f8e20f1")); - TbMsg requestMsg = TbMsg.newMsg(DataConstants.HP_QUEUE_NAME, TbMsgType.REST_API_REQUEST, tenantId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg requestMsg = TbMsg.builder() + .queueName(DataConstants.HP_QUEUE_NAME) + .type(TbMsgType.REST_API_REQUEST) + .originator(tenantId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); when(producerProvider.getRuleEngineMsgProducer()).thenReturn(tbREQueueProducer); @@ -305,7 +311,12 @@ public class DefaultTbClusterServiceTest { public void testPushMsgToRuleEngineWithTenantIdIsNullUuidAndEntityIsDevice() { TenantId tenantId = TenantId.SYS_TENANT_ID; DeviceId deviceId = new DeviceId(UUID.fromString("aa6d112d-2914-4a22-a9e3-bee33edbdb14")); - TbMsg requestMsg = TbMsg.newMsg(TbMsgType.REST_API_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg requestMsg = TbMsg.builder() + .type(TbMsgType.REST_API_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); TbQueueCallback callback = mock(TbQueueCallback.class); clusterService.pushMsgToRuleEngine(tenantId, deviceId, requestMsg, false, callback); @@ -321,7 +332,13 @@ public class DefaultTbClusterServiceTest { TenantId tenantId = TenantId.fromUUID(UUID.fromString("3c8bd350-1239-4a3b-b9c3-4dd76f8e20f1")); DeviceId deviceId = new DeviceId(UUID.fromString("adbb9d41-3367-40fd-9e74-7dd7cc5d30cf")); DeviceProfile deviceProfile = new DeviceProfile(new DeviceProfileId(UUID.fromString("552f5d6d-0b2b-43e1-a7d2-a51cb2a96927"))); - TbMsg requestMsg = TbMsg.newMsg(DataConstants.HP_QUEUE_NAME, TbMsgType.REST_API_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg requestMsg = TbMsg.builder() + .queueName(DataConstants.HP_QUEUE_NAME) + .type(TbMsgType.REST_API_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); when(deviceProfileCache.get(any(TenantId.class), any(DeviceId.class))).thenReturn(deviceProfile); when(producerProvider.getRuleEngineMsgProducer()).thenReturn(tbREQueueProducer); @@ -341,7 +358,12 @@ public class DefaultTbClusterServiceTest { DeviceId deviceId = new DeviceId(UUID.fromString("016c2abb-f46f-49f9-a83d-4d28b803cfe6")); DeviceProfile deviceProfile = new DeviceProfile(new DeviceProfileId(UUID.fromString("dc5766e2-1a32-4022-859b-743050097ab7"))); deviceProfile.setDefaultQueueName(DataConstants.MAIN_QUEUE_NAME); - TbMsg requestMsg = TbMsg.newMsg(TbMsgType.REST_API_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg requestMsg = TbMsg.builder() + .type(TbMsgType.REST_API_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); when(deviceProfileCache.get(any(TenantId.class), any(DeviceId.class))).thenReturn(deviceProfile); when(producerProvider.getRuleEngineMsgProducer()).thenReturn(tbREQueueProducer); @@ -375,12 +397,12 @@ public class DefaultTbClusterServiceTest { device.setDeviceProfileId(deviceProfileId); // device updated - TbMsg tbMsg = TbMsg.builder().internalType(TbMsgType.ENTITY_UPDATED).build(); + TbMsg tbMsg = TbMsg.builder().type(TbMsgType.ENTITY_UPDATED).build(); ((DefaultTbClusterService) clusterService).getRuleEngineProfileForEntityOrElseNull(tenantId, deviceId, tbMsg); verify(deviceProfileCache, times(1)).get(tenantId, deviceId); // device deleted - tbMsg = TbMsg.builder().internalType(TbMsgType.ENTITY_DELETED).data(JacksonUtil.toString(device)).build(); + tbMsg = TbMsg.builder().type(TbMsgType.ENTITY_DELETED).data(JacksonUtil.toString(device)).build(); ((DefaultTbClusterService) clusterService).getRuleEngineProfileForEntityOrElseNull(tenantId, deviceId, tbMsg); verify(deviceProfileCache, times(1)).get(tenantId, deviceProfileId); } @@ -395,12 +417,12 @@ public class DefaultTbClusterServiceTest { asset.setAssetProfileId(assetProfileId); // asset updated - TbMsg tbMsg = TbMsg.builder().internalType(TbMsgType.ENTITY_UPDATED).build(); + TbMsg tbMsg = TbMsg.builder().type(TbMsgType.ENTITY_UPDATED).build(); ((DefaultTbClusterService) clusterService).getRuleEngineProfileForEntityOrElseNull(tenantId, assetId, tbMsg); verify(assetProfileCache, times(1)).get(tenantId, assetId); // asset deleted - tbMsg = TbMsg.builder().internalType(TbMsgType.ENTITY_DELETED).data(JacksonUtil.toString(asset)).build(); + tbMsg = TbMsg.builder().type(TbMsgType.ENTITY_DELETED).data(JacksonUtil.toString(asset)).build(); ((DefaultTbClusterService) clusterService).getRuleEngineProfileForEntityOrElseNull(tenantId, assetId, tbMsg); verify(assetProfileCache, times(1)).get(tenantId, assetProfileId); } diff --git a/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManagerTest.java b/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManagerTest.java index 66e3de13d1..63f5f7999f 100644 --- a/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManagerTest.java +++ b/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManagerTest.java @@ -782,7 +782,12 @@ public class TbRuleEngineQueueConsumerManagerTest { } public void setUpTestMsg() { - testMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, new DeviceId(UUID.randomUUID()), new TbMsgMetaData(), "{}"); + testMsg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(new DeviceId(UUID.randomUUID())) + .metaData(new TbMsgMetaData().copy()) + .data("{}") + .build(); } } diff --git a/application/src/test/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcServiceTest.java b/application/src/test/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcServiceTest.java index 2cab5f991a..ac103d54e3 100644 --- a/application/src/test/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcServiceTest.java @@ -46,7 +46,12 @@ class DefaultTbRuleEngineRpcServiceTest { String serviceId = "tb-core-0"; UUID requestId = UUID.fromString("f64a20df-eb1e-46a3-ba6f-0b3ae053ee0a"); DeviceId deviceId = new DeviceId(UUID.fromString("1d9f771a-7cdc-4ac7-838c-ba193d05a012")); - TbMsg msg = TbMsg.newMsg(TbMsgType.REST_API_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.REST_API_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); var restApiCallResponseMsgProto = TransportProtos.RestApiCallResponseMsgProto.newBuilder() .setRequestIdMSB(requestId.getMostSignificantBits()) .setRequestIdLSB(requestId.getLeastSignificantBits()) diff --git a/application/src/test/java/org/thingsboard/server/service/ruleengine/DefaultRuleEngineCallServiceTest.java b/application/src/test/java/org/thingsboard/server/service/ruleengine/DefaultRuleEngineCallServiceTest.java index c49149bd07..48853c0e46 100644 --- a/application/src/test/java/org/thingsboard/server/service/ruleengine/DefaultRuleEngineCallServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/ruleengine/DefaultRuleEngineCallServiceTest.java @@ -85,7 +85,13 @@ public class DefaultRuleEngineCallServiceTest { metaData.put("serviceId", "core"); metaData.put("requestUUID", requestId.toString()); metaData.put("expirationTime", Long.toString(expTime)); - TbMsg msg = TbMsg.newMsg(DataConstants.MAIN_QUEUE_NAME, TbMsgType.REST_API_REQUEST, TENANT_ID, new TbMsgMetaData(metaData), "{\"key\":\"value\"}"); + TbMsg msg = TbMsg.builder() + .queueName(DataConstants.MAIN_QUEUE_NAME) + .type(TbMsgType.REST_API_REQUEST) + .originator(TENANT_ID) + .metaData(new TbMsgMetaData(metaData).copy()) + .data("{\"key\":\"value\"}") + .build(); Consumer anyConsumer = TbMsg::getData; doAnswer(invocation -> { @@ -113,7 +119,13 @@ public class DefaultRuleEngineCallServiceTest { metaData.put("serviceId", "core"); metaData.put("requestUUID", requestId.toString()); metaData.put("expirationTime", Long.toString(expTime)); - TbMsg msg = TbMsg.newMsg(DataConstants.MAIN_QUEUE_NAME, TbMsgType.REST_API_REQUEST, TENANT_ID, new TbMsgMetaData(metaData), "{\"key\":\"value\"}"); + TbMsg msg = TbMsg.builder() + .queueName(DataConstants.MAIN_QUEUE_NAME) + .type(TbMsgType.REST_API_REQUEST) + .originator(TENANT_ID) + .metaData(new TbMsgMetaData(metaData).copy()) + .data("{\"key\":\"value\"}") + .build(); Consumer anyConsumer = TbMsg::getData; doAnswer(invocation -> { diff --git a/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java b/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java index 02281abac3..5d06102a0b 100644 --- a/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java @@ -131,11 +131,13 @@ public class SequentialTimeseriesPersistenceTest extends AbstractControllerTest void saveLatestTsForAssetAndDevice(List devices, Asset asset, int idx) throws ExecutionException, InterruptedException, TimeoutException { for (Device device : devices) { - TbMsg tbMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, - device.getId(), - getTbMsgMetadata(device.getName(), ts.get(idx)), - TbMsgDataType.JSON, - getTbMsgData(msgValue.get(idx))); + TbMsg tbMsg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(device.getId()) + .metaData(getTbMsgMetadata(device.getName(), ts.get(idx)).copy()) + .dataType(TbMsgDataType.JSON) + .data(getTbMsgData(msgValue.get(idx))) + .build(); saveDeviceTsEntry(device.getId(), tbMsg, msgValue.get(idx)); saveAssetTsEntry(asset, device.getName(), msgValue.get(idx), TbMsgTimeseriesNode.computeTs(tbMsg, configuration.isUseServerTs())); idx++; diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java index 46c31cf245..b987a73a6e 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java @@ -20,7 +20,6 @@ import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; import lombok.AccessLevel; import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -47,7 +46,6 @@ import static java.util.Objects.requireNonNull; @Data @Slf4j @AllArgsConstructor(access = AccessLevel.PRIVATE) -@Builder(toBuilder = true) public final class TbMsg implements Serializable { public static final String EMPTY_JSON_OBJECT = "{}"; @@ -156,229 +154,6 @@ public final class TbMsg implements Serializable { tbMsg.getDataType(), tbMsg.getData(), ruleChainId, ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), TbMsgCallback.EMPTY); } - @Deprecated(since = "3.6.0", forRemoval = true) - public static TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return newMsg(queueName, type, originator, null, metaData, data, ruleChainId, ruleNodeId); - } - - /** - * Creates a new TbMsg instance with the specified parameters. - * - *

Deprecated: This method is deprecated since version 3.6.0 and should only be used when you need to - * specify a custom message type that doesn't exist in the {@link TbMsgType} enum. For standard message types, - * it is recommended to use the {@link #newMsg(String, TbMsgType, EntityId, CustomerId, TbMsgMetaData, String, RuleChainId, RuleNodeId)} - * method instead.

- * - * @param queueName the name of the queue where the message will be sent - * @param type the type of the message - * @param originator the originator of the message - * @param customerId the ID of the customer associated with the message - * @param metaData the metadata of the message - * @param data the data of the message - * @param ruleChainId the ID of the rule chain associated with the message - * @param ruleNodeId the ID of the rule node associated with the message - * @return new TbMsg instance - */ - @Deprecated(since = "3.6.0") - public static TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return TbMsg.builder() - .queueName(queueName) - .type(type) - .originator(originator) - .metaData(metaData.copy()) - .data(data) - .ruleChainId(ruleChainId) - .ruleNodeId(ruleNodeId) - .build(); - } - - @Deprecated(since = "3.6.0", forRemoval = true) - public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data) { - return newMsg(type, originator, null, metaData, data); - } - - @Deprecated(since = "3.6.0", forRemoval = true) - public static TbMsg newMsg(String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { - return TbMsg.builder() - .type(type) - .originator(originator) - .customerId(customerId) - .metaData(metaData.copy()) - .data(data) - .build(); - } - - public static TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return newMsg(queueName, type, originator, null, metaData, data, ruleChainId, ruleNodeId); - } - - public static TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return TbMsg.builder() - .queueName(queueName) - .internalType(type) - .originator(originator) - .customerId(customerId) - .metaData(metaData.copy()) - .data(data) - .ruleChainId(ruleChainId) - .ruleNodeId(ruleNodeId) - .build(); - } - - public static TbMsg newMsg(TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data) { - return newMsg(type, originator, null, metaData, data); - } - - public static TbMsg newMsg(TbMsgType type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { - return TbMsg.builder() - .internalType(type) - .originator(originator) - .customerId(customerId) - .metaData(metaData.copy()) - .data(data) - .build(); - } - - public static TbMsg newMsg(TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data, long ts) { - return TbMsg.builder() - .ts(ts) - .internalType(type) - .originator(originator) - .metaData(metaData.copy()) - .data(data) - .build(); - } - - // REALLY NEW MSG - - /** - * Creates a new TbMsg instance with the specified parameters. - * - *

Deprecated: This method is deprecated since version 3.6.0 and should only be used when you need to - * specify a custom message type that doesn't exist in the {@link TbMsgType} enum. For standard message types, - * it is recommended to use the {@link #newMsg(String, TbMsgType, EntityId, TbMsgMetaData, String)} - * method instead.

- * - * @param queueName the name of the queue where the message will be sent - * @param type the type of the message - * @param originator the originator of the message - * @param metaData the metadata of the message - * @param data the data of the message - * @return new TbMsg instance - */ - @Deprecated(since = "3.6.0") - public static TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data) { - return newMsg(queueName, type, originator, null, metaData, data); - } - - /** - * Creates a new TbMsg instance with the specified parameters. - * - *

Deprecated: This method is deprecated since version 3.6.0 and should only be used when you need to - * specify a custom message type that doesn't exist in the {@link TbMsgType} enum. For standard message types, - * it is recommended to use the {@link #newMsg(String, TbMsgType, EntityId, CustomerId, TbMsgMetaData, String)} - * method instead.

- * - * @param queueName the name of the queue where the message will be sent - * @param type the type of the message - * @param originator the originator of the message - * @param customerId the ID of the customer associated with the message - * @param metaData the metadata of the message - * @param data the data of the message - * @return new TbMsg instance - */ - @Deprecated(since = "3.6.0") - public static TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { - return TbMsg.builder() - .queueName(queueName) - .type(type) - .originator(originator) - .customerId(customerId) - .metaData(metaData.copy()) - .data(data) - .build(); - } - - /** - * Creates a new TbMsg instance with the specified parameters. - * - *

Deprecated: This method is deprecated since version 3.6.0 and should only be used when you need to - * specify a custom message type that doesn't exist in the {@link TbMsgType} enum. For standard message types, - * it is recommended to use the {@link #newMsg(TbMsgType, EntityId, TbMsgMetaData, TbMsgDataType, String)} - * method instead.

- * - * @param type the type of the message - * @param originator the originator of the message - * @param metaData the metadata of the message - * @param dataType the dataType of the message - * @param data the data of the message - * @return new TbMsg instance - */ - @Deprecated(since = "3.6.0") - public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data) { - return TbMsg.builder() - .type(type) - .originator(originator) - .customerId(null) - .metaData(metaData.copy()) - .dataType(dataType) - .data(data) - .build(); - } - - public static TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data) { - return newMsg(queueName, type, originator, null, metaData, data); - } - - public static TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { - return TbMsg.builder() - .queueName(queueName) - .internalType(type) - .originator(originator) - .customerId(customerId) - .metaData(metaData.copy()) - .data(data) - .build(); - } - - public static TbMsg newMsg(TbMsgType type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data) { - return TbMsg.builder() - .internalType(type) - .originator(originator) - .customerId(customerId) - .metaData(metaData.copy()) - .dataType(dataType) - .data(data) - .build(); - } - - public static TbMsg newMsg(TbMsgType type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data) { - return newMsg(type, originator, null, metaData, dataType, data); - } - - public static TbMsg newMsg(TbMsgType type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return TbMsg.builder() - .internalType(type) - .originator(originator) - .metaData(metaData.copy()) - .dataType(dataType) - .data(data) - .ruleChainId(ruleChainId) - .ruleNodeId(ruleNodeId) - .build(); - } - - public static TbMsg newMsg(TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data, TbMsgCallback callback) { - return TbMsg.builder() - .internalType(type) - .originator(originator) - .metaData(metaData.copy()) - .data(data) - .callback(callback) - .build(); - } - - @Builder private TbMsg(String queueName, UUID id, long ts, TbMsgType internalType, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId, UUID correlationId, Integer partition, TbMsgProcessingCtx ctx, TbMsgCallback callback) { this.id = id != null ? id : UUID.randomUUID(); @@ -390,7 +165,7 @@ public final class TbMsg implements Serializable { } this.internalType = internalType != null ? internalType : getInternalType(type); this.type = type != null ? type : this.internalType.name(); - this.originator = requireNonNull(originator, "msg originator is missing"); + this.originator = originator; if (customerId == null || customerId.isNullUid()) { if (originator != null && originator.getEntityType() == EntityType.CUSTOMER) { this.customerId = new CustomerId(originator.getId()); @@ -572,4 +347,150 @@ public final class TbMsg implements Serializable { return false; } + public static TbMsgBuilder builder() { + return new TbMsgBuilder(); + } + + public TbMsgBuilder toBuilder() { + return new TbMsgBuilder() + .queueName(this.queueName) + .id(this.id) + .ts(this.ts) + .type(this.type) + .type(this.internalType) + .originator(this.originator) + .customerId(this.customerId) + .metaData(this.metaData) + .dataType(this.dataType) + .data(this.data) + .ruleChainId(this.ruleChainId) + .ruleNodeId(this.ruleNodeId) + .correlationId(this.correlationId) + .partition(this.partition) + .ctx(this.ctx) + .callback(this.callback); + } + + public static class TbMsgBuilder { + + private String queueName; + private UUID id; + private long ts; + private String type; + private TbMsgType internalType; + private EntityId originator; + private CustomerId customerId; + private TbMsgMetaData metaData; + private TbMsgDataType dataType; + private String data; + private RuleChainId ruleChainId; + private RuleNodeId ruleNodeId; + private UUID correlationId; + private Integer partition; + private TbMsgProcessingCtx ctx; + private TbMsgCallback callback; + + TbMsgBuilder() {} + + public TbMsgBuilder queueName(String queueName) { + this.queueName = queueName; + return this; + } + + public TbMsgBuilder id(UUID id) { + this.id = id; + return this; + } + + public TbMsgBuilder ts(long ts) { + this.ts = ts; + return this; + } + + /** + *

Deprecated: This should only be used when you need to specify a custom message type that doesn't exist in the {@link TbMsgType} enum. + * Prefer using {@link #type(TbMsgType)} instead. + * + * */ + @Deprecated + public TbMsgBuilder type(String type) { + this.type = type; + return this; + } + + public TbMsgBuilder type(TbMsgType internalType) { + this.internalType = internalType; + return this; + } + + public TbMsgBuilder originator(EntityId originator) { + this.originator = originator; + return this; + } + + public TbMsgBuilder customerId(CustomerId customerId) { + this.customerId = customerId; + return this; + } + + public TbMsgBuilder metaData(TbMsgMetaData metaData) { + this.metaData = metaData; + return this; + } + + public TbMsgBuilder dataType(TbMsgDataType dataType) { + this.dataType = dataType; + return this; + } + + public TbMsgBuilder data(String data) { + this.data = data; + return this; + } + + public TbMsgBuilder ruleChainId(RuleChainId ruleChainId) { + this.ruleChainId = ruleChainId; + return this; + } + + public TbMsgBuilder ruleNodeId(RuleNodeId ruleNodeId) { + this.ruleNodeId = ruleNodeId; + return this; + } + + public TbMsgBuilder correlationId(UUID correlationId) { + this.correlationId = correlationId; + return this; + } + + public TbMsgBuilder partition(Integer partition) { + this.partition = partition; + return this; + } + + public TbMsgBuilder ctx(TbMsgProcessingCtx ctx) { + this.ctx = ctx; + return this; + } + + public TbMsgBuilder callback(TbMsgCallback callback) { + this.callback = callback; + return this; + } + + public TbMsg build() { + return new TbMsg(queueName, id, ts, type, internalType, originator, customerId, metaData, dataType, data, ruleChainId, ruleNodeId, correlationId, partition, ctx, callback); + } + + public String toString() { + return "TbMsg.TbMsgBuilder(queueName=" + this.queueName + ", id=" + this.id + ", ts=" + this.ts + + ", type=" + this.type + ", internalType=" + this.internalType + ", originator=" + this.originator + + ", customerId=" + this.customerId + ", metaData=" + this.metaData + ", dataType=" + this.dataType + + ", data=" + this.data + ", ruleChainId=" + this.ruleChainId + ", ruleNodeId=" + this.ruleNodeId + + ", correlationId=" + this.correlationId + ", partition=" + this.partition + ", ctx=" + this.ctx + + ", callback=" + this.callback + ")"; + } + + } + } diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java index 44f61270b4..e15d27a1e0 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java @@ -45,7 +45,6 @@ import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.device.data.PowerMode; -import org.thingsboard.server.common.data.exception.TenantNotFoundException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; @@ -1137,7 +1136,16 @@ public class DefaultTransportService extends TransportActivityManager implements queueName = deviceProfile.getDefaultQueueName(); } - TbMsg tbMsg = TbMsg.newMsg(queueName, tbMsgType, deviceId, customerId, metaData, gson.toJson(json), ruleChainId, null); + TbMsg tbMsg = TbMsg.builder() + .queueName(queueName) + .type(tbMsgType) + .originator(deviceId) + .customerId(customerId) + .metaData(metaData.copy()) + .data(gson.toJson(json)) + .ruleChainId(ruleChainId) + .ruleNodeId(null) + .build(); ruleEngineProducerService.sendToRuleEngine(ruleEngineMsgProducer, tenantId, tbMsg, new StatsCallback(callback, ruleEngineProducerStats)); ruleEngineProducerStats.incrementTotal(); } diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java index b517bb0051..fa8e51b51f 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java @@ -192,9 +192,6 @@ public interface TbContext { void ack(TbMsg tbMsg); - @Deprecated(since = "3.6.0", forRemoval = true) - TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data); - /** * Creates a new TbMsg instance with the specified parameters. * diff --git a/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/util/TbNodeUtilsTest.java b/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/util/TbNodeUtilsTest.java index 7da2efdf3f..895b527d6a 100644 --- a/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/util/TbNodeUtilsTest.java +++ b/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/util/TbNodeUtilsTest.java @@ -44,7 +44,12 @@ public class TbNodeUtilsTest { ObjectNode node = JacksonUtil.newObjectNode(); node.put("data_key", "data_value"); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TenantId.SYS_TENANT_ID, md, JacksonUtil.toString(node)); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(TenantId.SYS_TENANT_ID) + .metaData(md.copy()) + .data(JacksonUtil.toString(node)) + .build(); String result = TbNodeUtils.processPattern(pattern, msg); Assertions.assertEquals("ABC metadata_value data_value", result); } @@ -58,7 +63,12 @@ public class TbNodeUtilsTest { ObjectNode node = JacksonUtil.newObjectNode(); node.put("key", "data_value"); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TenantId.SYS_TENANT_ID, md, JacksonUtil.toString(node)); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(TenantId.SYS_TENANT_ID) + .metaData(md.copy()) + .data(JacksonUtil.toString(node)) + .build(); String result = TbNodeUtils.processPattern(pattern, msg); Assertions.assertEquals(pattern, result); } @@ -72,7 +82,12 @@ public class TbNodeUtilsTest { ObjectNode node = JacksonUtil.newObjectNode(); node.put("key", "data_value"); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TenantId.SYS_TENANT_ID, md, JacksonUtil.toString(node)); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(TenantId.SYS_TENANT_ID) + .metaData(md.copy()) + .data(JacksonUtil.toString(node)) + .build(); String result = TbNodeUtils.processPattern(pattern, msg); Assertions.assertEquals("ABC metadata_value data_value", result); } @@ -93,7 +108,12 @@ public class TbNodeUtilsTest { ObjectNode node = JacksonUtil.newObjectNode(); node.set("key1", key1Node); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TenantId.SYS_TENANT_ID, md, JacksonUtil.toString(node)); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(TenantId.SYS_TENANT_ID) + .metaData(md.copy()) + .data(JacksonUtil.toString(node)) + .build(); String result = TbNodeUtils.processPattern(pattern, msg); Assertions.assertEquals("ABC metadata_value value3", result); } @@ -114,7 +134,12 @@ public class TbNodeUtilsTest { ObjectNode node = JacksonUtil.newObjectNode(); node.set("key1", key1Node); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TenantId.SYS_TENANT_ID, md, JacksonUtil.toString(node)); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(TenantId.SYS_TENANT_ID) + .metaData(md.copy()) + .data(JacksonUtil.toString(node)) + .build(); String result = TbNodeUtils.processPattern(pattern, msg); Assertions.assertEquals("ABC metadata_value $[key1.key2[0].key3]", result); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java index 333aaaf7df..c6619b508c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java @@ -74,7 +74,14 @@ public class TbMsgCountNode implements TbNode { TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("delta", Long.toString(System.currentTimeMillis() - lastScheduledTs + delay)); - TbMsg tbMsg = TbMsg.newMsg(msg.getQueueName(), TbMsgType.POST_TELEMETRY_REQUEST, ctx.getTenantId(), msg.getCustomerId(), metaData, gson.toJson(telemetryJson)); + TbMsg tbMsg = TbMsg.builder() + .queueName(msg.getQueueName()) + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(ctx.getTenantId()) + .customerId(msg.getCustomerId()) + .metaData(metaData.copy()) + .data(gson.toJson(telemetryJson)) + .build(); ctx.enqueueForTellNext(tbMsg, TbNodeConnectionType.SUCCESS); scheduleTickMsg(ctx, tbMsg); } else { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java index 1afba3581f..e16528d5d3 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java @@ -154,12 +154,13 @@ public class TbMsgDeduplicationNode implements TbNode { iterator.remove(); } } - deduplicationResults.add(TbMsg.newMsg( - queueName, - config.getOutMsgType(), - deduplicationId, - getMetadata(), - getMergedData(pack))); + deduplicationResults.add(TbMsg.builder() + .queueName(queueName) + .type(config.getOutMsgType()) + .originator(deduplicationId) + .metaData(getMetadata().copy()) + .data(getMergedData(pack)) + .build()); } else { TbMsg resultMsg = null; boolean searchMin = DeduplicationStrategy.FIRST.equals(config.getStrategy()); @@ -176,13 +177,15 @@ public class TbMsgDeduplicationNode implements TbNode { } } if (resultMsg != null) { - deduplicationResults.add(TbMsg.newMsg( - queueName != null ? queueName : resultMsg.getQueueName(), - resultMsg.getType(), - resultMsg.getOriginator(), - resultMsg.getCustomerId(), - resultMsg.getMetaData(), - resultMsg.getData())); + String queueName1 = queueName != null ? queueName : resultMsg.getQueueName(); + deduplicationResults.add(TbMsg.builder() + .queueName(queueName1) + .type(resultMsg.getType()) + .originator(resultMsg.getOriginator()) + .customerId(resultMsg.getCustomerId()) + .metaData(resultMsg.getMetaData().copy()) + .data(resultMsg.getData()) + .build()); } } packBoundsOpt = findValidPack(msgList, deduplicationTimeoutMs); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java index b60a46ee0a..8331f066d8 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java @@ -65,14 +65,14 @@ public class TbMsgDelayNode implements TbNode { TbMsg pendingMsg = pendingMsgs.remove(UUID.fromString(msg.getData())); if (pendingMsg != null) { ctx.enqueueForTellNext( - TbMsg.newMsg( - pendingMsg.getQueueName(), - pendingMsg.getType(), - pendingMsg.getOriginator(), - pendingMsg.getCustomerId(), - pendingMsg.getMetaData(), - pendingMsg.getData() - ), + TbMsg.builder() + .queueName(pendingMsg.getQueueName()) + .type(pendingMsg.getType()) + .originator(pendingMsg.getOriginator()) + .customerId(pendingMsg.getCustomerId()) + .metaData(pendingMsg.getMetaData().copy()) + .data(pendingMsg.getData()) + .build(), TbNodeConnectionType.SUCCESS ); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java index c7b2d010a8..a1ff3dc2d4 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java @@ -29,6 +29,7 @@ import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.msg.TbMsgType; @@ -176,7 +177,14 @@ public class TbDeviceProfileNode implements TbNode { } protected void scheduleAlarmHarvesting(TbContext ctx, TbMsg msg) { - TbMsg periodicCheck = TbMsg.newMsg(TbMsgType.DEVICE_PROFILE_PERIODIC_SELF_MSG, ctx.getTenantId(), msg != null ? msg.getCustomerId() : null, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + CustomerId customerId = msg != null ? msg.getCustomerId() : null; + TbMsg periodicCheck = TbMsg.builder() + .type(TbMsgType.DEVICE_PROFILE_PERIODIC_SELF_MSG) + .originator(ctx.getTenantId()) + .customerId(customerId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); ctx.tellSelf(periodicCheck, TimeUnit.MINUTES.toMillis(1)); } @@ -201,7 +209,12 @@ public class TbDeviceProfileNode implements TbNode { } protected void onProfileUpdate(DeviceProfile profile) { - ctx.tellSelf(TbMsg.newMsg(TbMsgType.DEVICE_PROFILE_UPDATE_SELF_MSG, ctx.getTenantId(), TbMsgMetaData.EMPTY, profile.getId().getId().toString()), 0L); + ctx.tellSelf(TbMsg.builder() + .type(TbMsgType.DEVICE_PROFILE_UPDATE_SELF_MSG) + .originator(ctx.getTenantId()) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(profile.getId().getId().toString()) + .build(), 0L); } private void onDeviceUpdate(DeviceId deviceId, DeviceProfile deviceProfile) { @@ -210,7 +223,12 @@ public class TbDeviceProfileNode implements TbNode { if (deviceProfile != null) { msgData.put("deviceProfileId", deviceProfile.getId().getId().toString()); } - ctx.tellSelf(TbMsg.newMsg(TbMsgType.DEVICE_UPDATE_SELF_MSG, ctx.getTenantId(), TbMsgMetaData.EMPTY, JacksonUtil.toString(msgData)), 0L); + ctx.tellSelf(TbMsg.builder() + .type(TbMsgType.DEVICE_UPDATE_SELF_MSG) + .originator(ctx.getTenantId()) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(JacksonUtil.toString(msgData)) + .build(), 0L); } protected void invalidateDeviceProfileCache(DeviceId deviceId, String deviceJson) { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNodeTest.java index dde4808deb..728b1a88d7 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNodeTest.java @@ -306,7 +306,12 @@ class TbAssignToCustomerNodeTest extends AbstractRuleNodeUpgradeTest { } private TbMsg getTbMsg(EntityId originator) { - return TbMsg.newMsg(TbMsgType.NA, originator, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + return TbMsg.builder() + .type(TbMsgType.NA) + .originator(originator) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); } private EntityId toOriginator(EntityType type) { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbClearAlarmNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbClearAlarmNodeTest.java index afab0a0a99..177c2349fb 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbClearAlarmNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbClearAlarmNodeTest.java @@ -91,7 +91,12 @@ class TbClearAlarmNodeTest { void alarmCanBeCleared() { initWithClearAlarmScript(); metadata.putValue("key", "value"); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, msgOriginator, metadata, "{\"temperature\": 50}"); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(msgOriginator) + .metaData(metadata.copy()) + .data("{\"temperature\": 50}") + .build(); long oldEndDate = System.currentTimeMillis(); Alarm activeAlarm = Alarm.builder().type("SomeType").tenantId(tenantId).originator(msgOriginator).severity(AlarmSeverity.WARNING).endTs(oldEndDate).build(); @@ -143,7 +148,12 @@ class TbClearAlarmNodeTest { void alarmCanBeClearedWithAlarmOriginator() { initWithClearAlarmScript(); metadata.putValue("key", "value"); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, alarmOriginator, metadata, "{\"temperature\": 50}"); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(alarmOriginator) + .metaData(metadata.copy()) + .data("{\"temperature\": 50}") + .build(); long oldEndDate = System.currentTimeMillis(); AlarmId id = new AlarmId(alarmOriginator.getId()); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNodeTest.java index 4166c05a7a..b676816d4f 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNodeTest.java @@ -106,9 +106,12 @@ public class TbCopyAttributesToEntityViewNodeTest { public void givenExistingClientAttributes_whenOnMsg_thenCopyAttributesToView() { EntityView entityView = getEntityView(CLIENT_TELEMETRY_ENTITY_VIEW); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, DEVICE_ID, - new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name())), - "{\"clientAttribute1\": 100, \"clientAttribute2\": \"value2\"}"); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(DEVICE_ID) + .metaData(new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name())).copy()) + .data("{\"clientAttribute1\": 100, \"clientAttribute2\": \"value2\"}") + .build(); mockEntityViewLookup(entityView); when(ctxMock.getTelemetryService()).thenReturn(telemetryServiceMock); @@ -140,9 +143,12 @@ public class TbCopyAttributesToEntityViewNodeTest { public void givenExistingServerAttributesAndMsgTypeAttributesDeleted_whenOnMsg_thenDeleteAttributesFromView() { EntityView entityView = getEntityView(SERVER_TELEMETRY_ENTITY_VIEW); - TbMsg msg = TbMsg.newMsg( - ATTRIBUTES_DELETED, DEVICE_ID, new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name())), - "{\"attributes\": [\"serverAttribute1\"]}"); + TbMsg msg = TbMsg.builder() + .type(ATTRIBUTES_DELETED) + .originator(DEVICE_ID) + .metaData(new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name())).copy()) + .data("{\"attributes\": [\"serverAttribute1\"]}") + .build(); mockEntityViewLookup(entityView); when(ctxMock.getTelemetryService()).thenReturn(telemetryServiceMock); @@ -171,9 +177,12 @@ public class TbCopyAttributesToEntityViewNodeTest { public void givenNonMatchedSharedAttributesAndMsgTypeIsAttributesDeleted_whenOnMsg_thenNoAttributesDeleteFromView() { EntityView entityView = getEntityView(SHARED_TELEMETRY_ENTITY_VIEW); - TbMsg msg = TbMsg.newMsg( - TbMsgType.ATTRIBUTES_DELETED, DEVICE_ID, new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SHARED_SCOPE.name())), - "{\"attributes\": [\"anotherAttribute\"]}"); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.ATTRIBUTES_DELETED) + .originator(DEVICE_ID) + .metaData(new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SHARED_SCOPE.name())).copy()) + .data("{\"attributes\": [\"anotherAttribute\"]}") + .build(); mockEntityViewLookup(entityView); @@ -188,9 +197,12 @@ public class TbCopyAttributesToEntityViewNodeTest { public void givenNonMatchedAttributesAndMsgTypeIsPostAttributesRequest_whenOnMsg_thenCopyNoAttributesToView() { EntityView entityView = getEntityView(CLIENT_TELEMETRY_ENTITY_VIEW); - TbMsg msg = TbMsg.newMsg( - TbMsgType.POST_ATTRIBUTES_REQUEST, DEVICE_ID, new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name())), - "{\"clientAttribute2\": \"value2\"}"); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(DEVICE_ID) + .metaData(new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name())).copy()) + .data("{\"clientAttribute2\": \"value2\"}") + .build(); mockEntityViewLookup(entityView); when(ctxMock.getTelemetryService()).thenReturn(telemetryServiceMock); @@ -220,9 +232,12 @@ public class TbCopyAttributesToEntityViewNodeTest { ); mockEntityViewLookup(entityView); - TbMsg msg = TbMsg.newMsg( - ATTRIBUTES_DELETED, DEVICE_ID, new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name())), - "{\"attributes\": [\"serverAttribute1\"]}"); + TbMsg msg = TbMsg.builder() + .type(ATTRIBUTES_DELETED) + .originator(DEVICE_ID) + .metaData(new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name())).copy()) + .data("{\"attributes\": [\"serverAttribute1\"]}") + .build(); node.onMsg(ctxMock, msg); verify(entityViewServiceMock).findEntityViewsByTenantIdAndEntityIdAsync(eq(TENANT_ID), eq(DEVICE_ID)); @@ -233,7 +248,12 @@ public class TbCopyAttributesToEntityViewNodeTest { @ParameterizedTest @EnumSource(TbMsgType.class) public void givenMsgTypeAndEmptyMetadata_whenOnMsg_thenVerifyFailureMsg(TbMsgType msgType) { - TbMsg msg = TbMsg.newMsg(msgType, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(msgType) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java index c0e27231c4..a73aa72cec 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java @@ -163,7 +163,12 @@ class TbCreateAlarmNodeTest { var ruleNodeSelfId = new RuleNodeId(Uuids.timeBased()); - var incomingMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, msgOriginator, metadata, "{\"temperature\": 50}"); + var incomingMsg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(msgOriginator) + .metaData(metadata.copy()) + .data("{\"temperature\": 50}") + .build(); Alarm existingAlarm = null; @@ -317,7 +322,12 @@ class TbCreateAlarmNodeTest { var ruleNodeSelfId = new RuleNodeId(Uuids.timeBased()); - var incomingMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, msgOriginator, metadata, "{\"temperature\": 50, \"alarmType\": \"" + alarmType + "\"}"); + var incomingMsg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(msgOriginator) + .metaData(metadata.copy()) + .data("{\"temperature\": 50, \"alarmType\": \"" + alarmType + "\"}") + .build(); var existingClearedAlarm = Alarm.builder() .tenantId(tenantId) @@ -508,7 +518,12 @@ class TbCreateAlarmNodeTest { var ruleNodeSelfId = new RuleNodeId(Uuids.timeBased()); - var incomingMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, msgOriginator, metadata, "{\"temperature\": 50, \"alarmSeverity\": \"" + newAlarmSeverity.name() + "\"}"); + var incomingMsg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(msgOriginator) + .metaData(metadata.copy()) + .data("{\"temperature\": 50, \"alarmSeverity\": \"" + newAlarmSeverity.name() + "\"}") + .build(); var existingAlarmId = new AlarmId(Uuids.timeBased()); var existingActiveAlarm = Alarm.builder() @@ -680,7 +695,12 @@ class TbCreateAlarmNodeTest { var ruleNodeSelfId = new RuleNodeId(Uuids.timeBased()); - var incomingMsg = TbMsg.newMsg(TbMsgType.ALARM, msgOriginator, metadata, JacksonUtil.toString(alarmFromIncomingMessage)); + var incomingMsg = TbMsg.builder() + .type(TbMsgType.ALARM) + .originator(msgOriginator) + .metaData(metadata.copy()) + .data(JacksonUtil.toString(alarmFromIncomingMessage)) + .build(); var existingClearedAlarm = Alarm.builder() .tenantId(tenantId) @@ -867,7 +887,12 @@ class TbCreateAlarmNodeTest { .details(newAlarmDetails) .build(); - var incomingMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, msgOriginator, metadata, JacksonUtil.toString(alarmFromIncomingMessage)); + var incomingMsg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(msgOriginator) + .metaData(metadata.copy()) + .data(JacksonUtil.toString(alarmFromIncomingMessage)) + .build(); var existingAlarmId = new AlarmId(Uuids.timeBased()); var existingActiveAlarm = Alarm.builder() @@ -1048,7 +1073,12 @@ class TbCreateAlarmNodeTest { .details(alarmDetails) .build(); - var incomingMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, msgOriginator, metadata, JacksonUtil.toString(alarmFromIncomingMessage)); + var incomingMsg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(msgOriginator) + .metaData(metadata.copy()) + .data(JacksonUtil.toString(alarmFromIncomingMessage)) + .build(); var existingAlarmId = new AlarmId(Uuids.timeBased()); var existingActiveAlarm = Alarm.builder() @@ -1189,7 +1219,12 @@ class TbCreateAlarmNodeTest { // GIVEN config = config.defaultConfiguration(); - var incomingMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, msgOriginator, metadata, "{\"temperature\": 50}"); + var incomingMsg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(msgOriginator) + .metaData(metadata.copy()) + .data("{\"temperature\": 50}") + .build(); given(ctxMock.getTenantId()).willReturn(tenantId); given(ctxMock.getAlarmService()).willReturn(alarmServiceMock); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateRelationNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateRelationNodeTest.java index 86381634ac..d287bb8e01 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateRelationNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateRelationNodeTest.java @@ -486,7 +486,12 @@ public class TbCreateRelationNodeTest extends AbstractRuleNodeUpgradeTest { when(ctxMock.getRelationService()).thenReturn(relationServiceMock); var mockMethodCallsMap = mockEntityServiceCallsCreateEntityIfNotExistsEnabled(); - var entityCreatedMsg = TbMsg.newMsg(TbMsgType.ENTITY_CREATED, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + var entityCreatedMsg = TbMsg.builder() + .type(TbMsgType.ENTITY_CREATED) + .originator(entityId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); mockMethodCallsMap.get(entityType).accept(entity, entityCreatedMsg); when(relationServiceMock.checkRelationAsync(any(), any(), any(), any(), any())).thenReturn(Futures.immediateFuture(false)); @@ -676,7 +681,12 @@ public class TbCreateRelationNodeTest extends AbstractRuleNodeUpgradeTest { } private TbMsg getTbMsg(EntityId originator, TbMsgMetaData metaData) { - return TbMsg.newMsg(TbMsgType.NA, originator, metaData, TbMsg.EMPTY_JSON_OBJECT); + return TbMsg.builder() + .type(TbMsgType.NA) + .originator(originator) + .metaData(metaData.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); } private TbMsgMetaData getMetadataWithNameTemplate() { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbDeleteRelationNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbDeleteRelationNodeTest.java index 55461bdd7b..8e9295b3e0 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbDeleteRelationNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbDeleteRelationNodeTest.java @@ -560,7 +560,12 @@ public class TbDeleteRelationNodeTest extends AbstractRuleNodeUpgradeTest { } private TbMsg getTbMsg(EntityId originator, TbMsgMetaData metaData) { - return TbMsg.newMsg(TbMsgType.NA, originator, metaData, TbMsg.EMPTY_JSON_OBJECT); + return TbMsg.builder() + .type(TbMsgType.NA) + .originator(originator) + .metaData(metaData.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); } private TbMsgMetaData getMetadataWithNameTemplate() { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbDeviceStateNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbDeviceStateNodeTest.java index 9b3d2dc29b..1f8dcd61be 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbDeviceStateNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbDeviceStateNodeTest.java @@ -85,7 +85,12 @@ public class TbDeviceStateNodeTest { metaData.putValue("ts", String.valueOf(METADATA_TS)); var data = JacksonUtil.newObjectNode(); data.put("humidity", 58.3); - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, JacksonUtil.toString(data)); + msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(metaData.copy()) + .data(JacksonUtil.toString(data)) + .build(); } @BeforeEach @@ -207,7 +212,12 @@ public class TbDeviceStateNodeTest { return unsupportedType; } }; - var msg = TbMsg.newMsg(TbMsgType.ENTITY_CREATED, nonDeviceOriginator, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + var msg = TbMsg.builder() + .type(TbMsgType.ENTITY_CREATED) + .originator(nonDeviceOriginator) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); // WHEN node.onMsg(ctxMock, msg); @@ -236,7 +246,13 @@ public class TbDeviceStateNodeTest { given(ctxMock.getDeviceStateManager()).willReturn(deviceStateManagerMock); long msgTs = METADATA_TS + 1; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT, msgTs); + msg = TbMsg.builder() + .ts(msgTs) + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); // WHEN node.onMsg(ctxMock, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbLogNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbLogNodeTest.java index 7d915f1cc9..63e5639d43 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbLogNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbLogNodeTest.java @@ -50,7 +50,12 @@ public class TbLogNodeTest { TbLogNode node = new TbLogNode(); String data = "{\"key\": \"value\"}"; TbMsgMetaData metaData = new TbMsgMetaData(Map.of("mdKey1", "mdValue1", "mdKey2", "23")); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TenantId.SYS_TENANT_ID, metaData, data); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(TenantId.SYS_TENANT_ID) + .metaData(metaData.copy()) + .data(data) + .build(); String logMessage = node.toLogMessage(msg); log.info(logMessage); @@ -66,7 +71,12 @@ public class TbLogNodeTest { void givenEmptyDataMsg_whenToLog_thenReturnString() { TbLogNode node = new TbLogNode(); TbMsgMetaData metaData = new TbMsgMetaData(Collections.emptyMap()); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TenantId.SYS_TENANT_ID, metaData, ""); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(TenantId.SYS_TENANT_ID) + .metaData(metaData.copy()) + .data("") + .build(); String logMessage = node.toLogMessage(msg); log.info(logMessage); @@ -82,7 +92,12 @@ public class TbLogNodeTest { void givenNullDataMsg_whenToLog_thenReturnString() { TbLogNode node = new TbLogNode(); TbMsgMetaData metaData = new TbMsgMetaData(Collections.emptyMap()); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TenantId.SYS_TENANT_ID, metaData, null); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(TenantId.SYS_TENANT_ID) + .metaData(metaData.copy()) + .data(null) + .build(); String logMessage = node.toLogMessage(msg); log.info(logMessage); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbMsgCountNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbMsgCountNodeTest.java index e1f020f5ff..3c71b09b7d 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbMsgCountNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbMsgCountNodeTest.java @@ -63,7 +63,12 @@ public class TbMsgCountNodeTest { private final DeviceId DEVICE_ID = new DeviceId(UUID.fromString("1b21c7cc-0c9e-4ab1-b867-99451599e146")); private final TenantId TENANT_ID = TenantId.fromUUID(UUID.fromString("04dfbd38-10e5-47b7-925f-11e795db89e1")); - private final TbMsg tickMsg = TbMsg.newMsg(TbMsgType.MSG_COUNT_SELF_MSG, RULE_NODE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + private final TbMsg tickMsg = TbMsg.builder() + .type(TbMsgType.MSG_COUNT_SELF_MSG) + .originator(RULE_NODE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); private ScheduledExecutorService executorService; private TbMsgCountNode node; @@ -120,7 +125,12 @@ public class TbMsgCountNodeTest { var expectedProcessedMsgs = new ArrayList(); for (int i = 0; i < msgCount; i++) { - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + var msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); if (msgWithCounterSent.get()) { break; } @@ -142,7 +152,12 @@ public class TbMsgCountNodeTest { then(ctxMock).should().enqueueForTellNext(msgWithCounterCaptor.capture(), eq(TbNodeConnectionType.SUCCESS)); TbMsg resultedMsg = msgWithCounterCaptor.getValue(); String expectedData = "{\"messageCount_tb-rule-engine\":" + currentMsgNumber + "}"; - TbMsg expectedMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TENANT_ID, TbMsgMetaData.EMPTY, expectedData); + TbMsg expectedMsg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(TENANT_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(expectedData) + .build(); assertThat(resultedMsg).usingRecursiveComparison() .ignoringFields("id", "ts", "ctx", "metaData") .isEqualTo(expectedMsg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNodeTest.java index 7df2a982de..e211ee6514 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNodeTest.java @@ -185,7 +185,12 @@ public class TbSaveToCustomCassandraTableNodeTest extends AbstractRuleNodeUpgrad node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); assertThatThrownBy(() -> node.onMsg(ctxMock, msg)) .isInstanceOf(IllegalStateException.class) .hasMessage("Invalid message structure, it is not a JSON Object: " + null); @@ -206,7 +211,12 @@ public class TbSaveToCustomCassandraTableNodeTest extends AbstractRuleNodeUpgrad "humidity": 77 } """; - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, data); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(data) + .build(); assertThatThrownBy(() -> node.onMsg(ctxMock, msg)) .isInstanceOf(RuntimeException.class) .hasMessage("Message data doesn't contain key: 'temp'!"); @@ -227,7 +237,12 @@ public class TbSaveToCustomCassandraTableNodeTest extends AbstractRuleNodeUpgrad "temp": [value] } """; - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, data); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(data) + .build(); assertThatThrownBy(() -> node.onMsg(ctxMock, msg)) .isInstanceOf(RuntimeException.class) .hasMessage("Message data key: 'temp' with value: '[\"value\"]' is not a JSON Object or JSON Primitive!"); @@ -249,7 +264,12 @@ public class TbSaveToCustomCassandraTableNodeTest extends AbstractRuleNodeUpgrad mockSubmittingCassandraTask(); node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); then(sessionMock).should().prepare(expectedQuery); @@ -299,7 +319,12 @@ public class TbSaveToCustomCassandraTableNodeTest extends AbstractRuleNodeUpgrad } } """; - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, data); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(data) + .build(); node.onMsg(ctxMock, msg); verifySettingStatementBuilder(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNodeTest.java index 8abef77289..65be0c5cff 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNodeTest.java @@ -294,7 +294,12 @@ class TbUnassignFromCustomerNodeTest extends AbstractRuleNodeUpgradeTest { } private TbMsg getTbMsg(EntityId originator) { - return TbMsg.newMsg(TbMsgType.NA, originator, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + return TbMsg.builder() + .type(TbMsgType.NA) + .originator(originator) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); } private static EntityId toOriginator(EntityType type) { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNodeTest.java index 14ae9bef20..7a96720292 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNodeTest.java @@ -145,7 +145,12 @@ public class TbAwsLambdaNodeTest { config.setFunctionName(functionName); config.setQualifier(qualifier); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metadata, data); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(metadata.copy()) + .data(data) + .build(); InvokeRequest request = createInvokeRequest(msg); String requestIdStr = "a124af57-e7c3-4ebb-83bf-b09ff86eaa23"; @@ -197,7 +202,12 @@ public class TbAwsLambdaNodeTest { init(); config.setTellFailureIfFuncThrowsExc(true); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_ARRAY) + .build(); InvokeRequest request = createInvokeRequest(msg); String requestIdStr = "a124af57-e7c3-4ebb-83bf-b09ff86eaa23"; String errorMsg = "Unhandled exception from function"; @@ -233,7 +243,12 @@ public class TbAwsLambdaNodeTest { public void givenExceptionWasThrownInsideFunctionAndTellFailureIfFuncThrowsExcIsFalse_whenOnMsg_thenTellSuccess() { init(); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); InvokeRequest request = createInvokeRequest(msg); String requestIdStr = "e83dfbc4-68d5-441c-8ee9-289959a30d3b"; String payload = "{\"errorMessage\":\"Something went wrong\",\"errorType\":\"Exception\",\"requestId\":\"" + requestIdStr + "\"}"; @@ -266,7 +281,12 @@ public class TbAwsLambdaNodeTest { public void givenPayloadFromResultIsNull_whenOnMsg_thenTellFailure() { init(); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); InvokeRequest request = createInvokeRequest(msg); String requestIdStr = "12bbb074-e2fc-4381-8f28-d4bd235103d5"; String errorMsg = "Payload from result of AWS Lambda function execution is null."; @@ -300,7 +320,12 @@ public class TbAwsLambdaNodeTest { @Test public void givenExceptionWasThrownOnAWS_whenOnMsg_thenTellFailure() { init(); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); InvokeRequest request = createInvokeRequest(msg); String errorMsg = "Simulated error"; diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sns/TbSnsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sns/TbSnsNodeTest.java index eaa20e7b59..d6161a14ae 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sns/TbSnsNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sns/TbSnsNodeTest.java @@ -105,7 +105,12 @@ class TbSnsNodeTest { given(publishResultMock.getSdkResponseMetadata()).willReturn(responseMetadataMock); given(responseMetadataMock.getRequestId()).willReturn(requestId); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, data); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(metaData.copy()) + .data(data) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should().ack(msg); @@ -143,7 +148,12 @@ class TbSnsNodeTest { ListenableFuture failedFuture = Futures.immediateFailedFuture(new RuntimeException(errorMsg)); given(listeningExecutor.executeAsync(any(Callable.class))).willReturn(failedFuture); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should(never()).enqueueForTellNext(any(), any(String.class)); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNodeTest.java index e8238ce5af..f32ddf665c 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNodeTest.java @@ -107,7 +107,12 @@ class TbSqsNodeTest { mockSendingMsgRequest(); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, data); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(metaData.copy()) + .data(data) + .build(); node.onMsg(ctxMock, msg); SendMessageRequest sendMsgRequest = new SendMessageRequest() @@ -143,7 +148,12 @@ class TbSqsNodeTest { mockSendingMsgRequest(); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, data); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(metaData.copy()) + .data(data) + .build(); node.onMsg(ctxMock, msg); Map messageAttributes = new HashMap<>(); @@ -186,7 +196,12 @@ class TbSqsNodeTest { given(sendMessageResultMock.getMD5OfMessageAttributes()).willReturn(messageAttributesMd5); given(sendMessageResultMock.getSequenceNumber()).willReturn(sequenceNumber); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should().ack(msg); @@ -221,7 +236,12 @@ class TbSqsNodeTest { ListenableFuture failedFuture = Futures.immediateFailedFuture(new RuntimeException(errorMsg)); given(listeningExecutor.executeAsync(any(Callable.class))).willReturn(failedFuture); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should(never()).enqueueForTellNext(any(), any(String.class)); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNodeTest.java index 53b8e5f814..9d1ede5bd6 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNodeTest.java @@ -190,7 +190,12 @@ public class TbMsgGeneratorNodeTest extends AbstractRuleNodeUpgradeTest { given(ctxMock.createScriptEngine(any(), any(), any(), any(), any())).willReturn(scriptEngineMock); // creation of tickMsg - TbMsg tickMsg = TbMsg.newMsg(TbMsgType.GENERATOR_NODE_SELF_MSG, RULE_NODE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg tickMsg = TbMsg.builder() + .type(TbMsgType.GENERATOR_NODE_SELF_MSG) + .originator(RULE_NODE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); given(ctxMock.newMsg(null, TbMsgType.GENERATOR_NODE_SELF_MSG, RULE_NODE_ID, null, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING)).willReturn(tickMsg); // invocation of tellSelf() method @@ -203,16 +208,31 @@ public class TbMsgGeneratorNodeTest extends AbstractRuleNodeUpgradeTest { }).given(ctxMock).tellSelf(any(), any(Long.class)); // creation of first message - TbMsg firstMsg = TbMsg.newMsg(TbMsg.EMPTY_STRING, RULE_NODE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg firstMsg = TbMsg.builder() + .type(TbMsg.EMPTY_STRING) + .originator(RULE_NODE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); given(ctxMock.newMsg(null, TbMsg.EMPTY_STRING, RULE_NODE_ID, null, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT)).willReturn(firstMsg); // creation of generated message TbMsgMetaData metaData = new TbMsgMetaData(Map.of("data", "40")); - TbMsg generatedMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, RULE_NODE_ID, metaData, "{ \"temp\": 42, \"humidity\": 77 }"); + TbMsg generatedMsg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(RULE_NODE_ID) + .metaData(metaData.copy()) + .data("{ \"temp\": 42, \"humidity\": 77 }") + .build(); given(scriptEngineMock.executeGenerateAsync(any())).willReturn(Futures.immediateFuture(generatedMsg)); // creation of prev message - TbMsg prevMsg = TbMsg.newMsg(generatedMsg.getType(), RULE_NODE_ID, generatedMsg.getMetaData(), generatedMsg.getData()); + TbMsg prevMsg = TbMsg.builder() + .type(generatedMsg.getType()) + .originator(RULE_NODE_ID) + .metaData(generatedMsg.getMetaData().copy()) + .data(generatedMsg.getData()) + .build(); given(ctxMock.newMsg(null, generatedMsg.getType(), RULE_NODE_ID, null, generatedMsg.getMetaData(), generatedMsg.getData())).willReturn(prevMsg); // WHEN diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNodeTest.java index 621a1a4ba2..e45e5fc0f1 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNodeTest.java @@ -85,8 +85,15 @@ public class TbMsgPushToEdgeNodeTest { Mockito.when(ctx.getEdgeService()).thenReturn(edgeService); Mockito.when(edgeService.findRelatedEdgeIdsByEntityId(tenantId, deviceId, new PageLink(RELATED_EDGES_CACHE_ITEMS))).thenReturn(new PageData<>()); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, null, null); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(TbMsg.EMPTY_JSON_OBJECT) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg); @@ -106,8 +113,15 @@ public class TbMsgPushToEdgeNodeTest { PageData edgePageData = new PageData<>(List.of(edgeId), 1, 1, false); Mockito.when(edgeService.findRelatedEdgeIdsByEntityId(tenantId, userId, new PageLink(RELATED_EDGES_CACHE_ITEMS))).thenReturn(edgePageData); - TbMsg msg = TbMsg.newMsg(TbMsgType.ATTRIBUTES_UPDATED, userId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, null, null); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.ATTRIBUTES_UPDATED) + .originator(userId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(TbMsg.EMPTY_JSON_OBJECT) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg); @@ -137,8 +151,15 @@ public class TbMsgPushToEdgeNodeTest { Mockito.when(ctx.getDbCallbackExecutor()).thenReturn(dbCallbackExecutor); Mockito.when(edgeEventService.saveAsync(any())).thenReturn(SettableFuture.create()); - TbMsg msg = TbMsg.newMsg(event, new EdgeId(UUID.randomUUID()), metaData, - TbMsgDataType.JSON, "{\"lastConnectTs\":1}", null, null); + TbMsg msg = TbMsg.builder() + .type(event) + .originator(new EdgeId(UUID.randomUUID())) + .metaData(metaData.copy()) + .dataType(TbMsgDataType.JSON) + .data("{\"lastConnectTs\":1}") + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNodeTest.java index f072eb1723..22c7997983 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNodeTest.java @@ -118,7 +118,13 @@ class TbAssetTypeSwitchNodeTest { } private TbMsg getTbMsg(EntityId entityId) { - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT, callback); + return TbMsg.builder() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .callback(callback) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java index 5b71db5dfb..5aa9b9769f 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java @@ -174,7 +174,12 @@ class TbCheckAlarmStatusNodeTest { } private TbMsg getTbMsg(String msgData) { - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, msgData); + return TbMsg.builder() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(msgData) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckMessageNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckMessageNodeTest.java index ce8afbbe99..a1dcd7d526 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckMessageNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckMessageNodeTest.java @@ -44,7 +44,12 @@ import static org.mockito.Mockito.verify; class TbCheckMessageNodeTest { private static final DeviceId DEVICE_ID = new DeviceId(UUID.randomUUID()); - private static final TbMsg EMPTY_POST_ATTRIBUTES_MSG = TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + private static final TbMsg EMPTY_POST_ATTRIBUTES_MSG = TbMsg.builder() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); private TbCheckMessageNode node; @@ -196,7 +201,12 @@ class TbCheckMessageNodeTest { metadata.putValue(DataConstants.DEVICE_NAME, "Test Device"); metadata.putValue(DataConstants.DEVICE_TYPE, DataConstants.DEFAULT_DEVICE_TYPE); metadata.putValue("ts", String.valueOf(System.currentTimeMillis())); - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, DEVICE_ID, metadata, data); + return TbMsg.builder() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(DEVICE_ID) + .metaData(metadata.copy()) + .data(data) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeTest.java index e445b93455..d6a9f98ac0 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeTest.java @@ -66,7 +66,12 @@ class TbCheckRelationNodeTest extends AbstractRuleNodeUpgradeTest { private final TenantId TENANT_ID = new TenantId(UUID.randomUUID()); private final DeviceId ORIGINATOR_ID = new DeviceId(UUID.randomUUID()); private final TestDbCallbackExecutor DB_EXECUTOR = new TestDbCallbackExecutor(); - private final TbMsg EMPTY_POST_ATTRIBUTES_MSG = TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, ORIGINATOR_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + private final TbMsg EMPTY_POST_ATTRIBUTES_MSG = TbMsg.builder() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(ORIGINATOR_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); private TbCheckRelationNode node; diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbDeviceTypeSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbDeviceTypeSwitchNodeTest.java index 707df26d0f..ab446f3215 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbDeviceTypeSwitchNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbDeviceTypeSwitchNodeTest.java @@ -118,6 +118,12 @@ class TbDeviceTypeSwitchNodeTest { } private TbMsg getTbMsg(EntityId entityId) { - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT, callback); + return TbMsg.builder() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .callback(callback) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java index b991b7b220..c38461d08b 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java @@ -59,7 +59,15 @@ public class TbJsFilterNodeTest { @Test public void falseEvaluationDoNotSendMsg() throws TbNodeException { initWithScript(); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, null, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(null) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(TbMsg.EMPTY_JSON_OBJECT) + .ruleChainId(ruleChainId) + .ruleNodeId(ruleNodeId) + .build(); when(scriptEngine.executeFilterAsync(msg)).thenReturn(Futures.immediateFuture(false)); node.onMsg(ctx, msg); @@ -71,7 +79,15 @@ public class TbJsFilterNodeTest { public void exceptionInJsThrowsException() throws TbNodeException { initWithScript(); TbMsgMetaData metaData = new TbMsgMetaData(); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, null, metaData, TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(null) + .metaData(metaData.copy()) + .dataType(TbMsgDataType.JSON) + .data(TbMsg.EMPTY_JSON_OBJECT) + .ruleChainId(ruleChainId) + .ruleNodeId(ruleNodeId) + .build(); when(scriptEngine.executeFilterAsync(msg)).thenReturn(Futures.immediateFailedFuture(new ScriptException("error"))); @@ -83,7 +99,15 @@ public class TbJsFilterNodeTest { public void metadataConditionCanBeTrue() throws TbNodeException { initWithScript(); TbMsgMetaData metaData = new TbMsgMetaData(); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, null, metaData, TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(null) + .metaData(metaData.copy()) + .dataType(TbMsgDataType.JSON) + .data(TbMsg.EMPTY_JSON_OBJECT) + .ruleChainId(ruleChainId) + .ruleNodeId(ruleNodeId) + .build(); when(scriptEngine.executeFilterAsync(msg)).thenReturn(Futures.immediateFuture(true)); node.onMsg(ctx, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java index ff7a40644b..908e941355 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java @@ -59,7 +59,15 @@ public class TbJsSwitchNodeTest { metaData.putValue("humidity", "99"); String rawJson = "{\"name\": \"Vit\", \"passed\": 5}"; - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, null, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(null) + .metaData(metaData.copy()) + .dataType(TbMsgDataType.JSON) + .data(rawJson) + .ruleChainId(ruleChainId) + .ruleNodeId(ruleNodeId) + .build(); when(scriptEngine.executeSwitchAsync(msg)).thenReturn(Futures.immediateFuture(Sets.newHashSet("one", "three"))); node.onMsg(ctx, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNodeTest.java index c760a295e2..554b41aebf 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNodeTest.java @@ -97,7 +97,12 @@ class TbMsgTypeFilterNodeTest { } private TbMsg getTbMsg(EntityId entityId, TbMsgType msgType) { - return TbMsg.newMsg(msgType, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + return TbMsg.builder() + .type(msgType) + .originator(entityId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNodeTest.java index cc771de1d1..2668588f91 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNodeTest.java @@ -89,7 +89,12 @@ class TbMsgTypeSwitchNodeTest { } private TbMsg getTbMsg(TbMsgType msgType) { - return TbMsg.newMsg(msgType, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + return TbMsg.builder() + .type(msgType) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNodeTest.java index b1f6c59407..e3bc8662d7 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNodeTest.java @@ -96,7 +96,12 @@ class TbOriginatorTypeFilterNodeTest { } private TbMsg getTbMsg(EntityId entityId) { - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + return TbMsg.builder() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNodeTest.java index fd67fa16b8..914e6dfee3 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNodeTest.java @@ -90,7 +90,12 @@ class TbOriginatorTypeSwitchNodeTest { } private TbMsg getTbMsg(EntityId entityId) { - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + return TbMsg.builder() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbAckNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbAckNodeTest.java index 49e92f2d74..08b3eff39a 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbAckNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbAckNodeTest.java @@ -67,7 +67,12 @@ public class TbAckNodeTest { public void givenMsg_whenOnMsg_thenAckAndTellSuccess() throws TbNodeException { node.init(ctxMock, nodeConfiguration); DeviceId deviceId = new DeviceId(UUID.fromString("5770153d-6ca2-4447-8a54-5d8a4538e052")); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should().ack(msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeTest.java index e596c71d1e..777c93c3f5 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeTest.java @@ -87,7 +87,12 @@ public class TbCheckpointNodeTest extends AbstractRuleNodeUpgradeTest { given(ctxMock.getQueueName()).willReturn(queueName); node.init(ctxMock, nodeConfiguration); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor onSuccess = ArgumentCaptor.forClass(Runnable.class); @@ -101,7 +106,12 @@ public class TbCheckpointNodeTest extends AbstractRuleNodeUpgradeTest { given(ctxMock.getQueueName()).willReturn(DataConstants.HP_QUEUE_NAME); node.init(ctxMock, nodeConfiguration); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor> onFailure = ArgumentCaptor.forClass(Consumer.class); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainInputNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainInputNodeTest.java index 680d106d1e..91e89afd2e 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainInputNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainInputNodeTest.java @@ -300,6 +300,11 @@ public class TbRuleChainInputNodeTest extends AbstractRuleNodeUpgradeTest { } private TbMsg getMsg(EntityId entityId) { - return TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + return TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(entityId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainOutputNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainOutputNodeTest.java index 5c678e5c50..623e40c130 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainOutputNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainOutputNodeTest.java @@ -74,7 +74,12 @@ public class TbRuleChainOutputNodeTest { node.init(ctxMock, nodeConfiguration); DeviceId deviceId = new DeviceId(UUID.fromString("f514da88-79b3-46da-9f02-1747c5e84f44")); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should().output(msg, "test"); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNodeTest.java index 309023b3c6..94e7e36384 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNodeTest.java @@ -118,7 +118,12 @@ class TbPubSubNodeTest { given(ctxMock.getExternalCallExecutor()).willReturn(executor); node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, data); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(metaData.copy()) + .data(data) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should().ack(msg); @@ -164,7 +169,12 @@ class TbPubSubNodeTest { node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); TbMsgMetaData metadata = new TbMsgMetaData(); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metadata, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(metadata.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should(never()).ack(msg); @@ -193,7 +203,12 @@ class TbPubSubNodeTest { node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); TbMsgMetaData metaData = new TbMsgMetaData(); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(metaData.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should(never()).ack(any()); @@ -221,7 +236,12 @@ class TbPubSubNodeTest { node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); TbMsgMetaData metaData = new TbMsgMetaData(); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(metaData.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should().ack(msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeTest.java index a0b57ba948..cb5c7fa87c 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeTest.java @@ -164,7 +164,12 @@ class TbGpsGeofencingActionNodeTest extends AbstractRuleNodeUpgradeTest { private TbMsg getTbMsg(EntityId entityId, TbMsgMetaData metadata, double latitude, double longitude) { String data = "{\"latitude\": " + latitude + ", \"longitude\": " + longitude + "}"; - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, metadata, data); + return TbMsg.builder() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .metaData(metadata.copy()) + .data(data) + .build(); } private TbMsgMetaData getMetadataForNewVersionPolygonPerimeter() { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeTest.java index 10968516e6..fe54dec117 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeTest.java @@ -448,11 +448,21 @@ class TbGpsGeofencingFilterNodeTest { private TbMsg getTbMsg(EntityId entityId, TbMsgMetaData metadata, double latitude, double longitude) { String data = "{\"latitude\": " + latitude + ", \"longitude\": " + longitude + "}"; - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, metadata, data); + return TbMsg.builder() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .metaData(metadata.copy()) + .data(data) + .build(); } private TbMsg getEmptyArrayTbMsg(EntityId entityId) { - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + return TbMsg.builder() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_ARRAY) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeTest.java index add12dcec2..196b2aa8b8 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeTest.java @@ -188,7 +188,12 @@ public class TbKafkaNodeTest { ReflectionTestUtils.setField(node, "initError", new ThingsboardKafkaClientError(errorMsg)); // WHEN - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); // THEN @@ -212,7 +217,12 @@ public class TbKafkaNodeTest { // WHEN node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); // THEN @@ -232,7 +242,12 @@ public class TbKafkaNodeTest { // GIVEN config.setTopicPattern(topicPattern); config.setKeyPattern(keyPattern); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, data); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(metaData.copy()) + .data(data) + .build(); String topic = TbNodeUtils.processPattern(topicPattern, msg); String key = TbNodeUtils.processPattern(keyPattern, msg); @@ -278,7 +293,12 @@ public class TbKafkaNodeTest { // WHEN node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); // THEN @@ -303,7 +323,12 @@ public class TbKafkaNodeTest { // WHEN node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); // THEN @@ -331,7 +356,12 @@ public class TbKafkaNodeTest { node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("key", "value"); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(metaData.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); // THEN diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeTest.java index 5ea816a1eb..4036dfd6b0 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeTest.java @@ -103,7 +103,12 @@ public class TbMsgToEmailNodeTest { } var msgDataStr = "{\"temperature\": " + EXPECTED_TEMPERATURE + "}"; - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, md, msgDataStr); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .metaData(md.copy()) + .data(msgDataStr) + .build(); // WHEN node.onMsg(ctxMock, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java index ba81fbad20..17256cfac0 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java @@ -173,7 +173,12 @@ public class TbMathNodeTest { metaData.putValue("key2", "argumentA"); ObjectNode msgNode = JacksonUtil.newObjectNode() .put("key3", "argumentB").put("argumentA", 2).put("argumentB", 2); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, msgNode.toString()); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .metaData(metaData.copy()) + .data(msgNode.toString()) + .build(); node.onMsg(ctx, msg); @@ -181,7 +186,12 @@ public class TbMathNodeTest { metaData.putValue("key2", "argumentC"); msgNode = JacksonUtil.newObjectNode() .put("key3", "argumentD").put("argumentC", 4).put("argumentD", 3); - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, msgNode.toString()); + msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .metaData(metaData.copy()) + .data(msgNode.toString()) + .build(); node.onMsg(ctx, msg); @@ -228,7 +238,12 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "b") ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", arg1).put("b", arg2).toString()); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(JacksonUtil.newObjectNode().put("a", arg1).put("b", arg2).toString()) + .build(); node.onMsg(ctx, msg); @@ -291,7 +306,12 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", arg1).toString()); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(JacksonUtil.newObjectNode().put("a", arg1).toString()) + .build(); node.onMsg(ctx, msg); @@ -314,7 +334,12 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "b") ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString()); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString()) + .build(); node.onMsg(ctx, msg); @@ -337,7 +362,12 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "b") ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString()); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString()) + .build(); node.onMsg(ctx, msg); @@ -361,7 +391,12 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.TIME_SERIES, "b") ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().toString()); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(JacksonUtil.newObjectNode().toString()) + .build(); when(attributesService.find(tenantId, originator, AttributeScope.SERVER_SCOPE, "a")) .thenReturn(Futures.immediateFuture(Optional.of(new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("a", 2.0))))); @@ -389,7 +424,12 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 5).toString()); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(JacksonUtil.newObjectNode().put("a", 5).toString()) + .build(); node.onMsg(ctx, msg); @@ -411,7 +451,12 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 5).toString()); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(JacksonUtil.newObjectNode().put("a", 5).toString()) + .build(); node.onMsg(ctx, msg); @@ -433,7 +478,12 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 5).toString()); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(JacksonUtil.newObjectNode().put("a", 5).toString()) + .build(); when(telemetryService.saveAttrAndNotify(any(), any(), any(AttributeScope.class), anyString(), anyDouble())) .thenReturn(Futures.immediateFuture(null)); @@ -459,7 +509,12 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 5).toString()); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(JacksonUtil.newObjectNode().put("a", 5).toString()) + .build(); when(telemetryService.saveAndNotify(any(), any(), any(TsKvEntry.class))) .thenReturn(Futures.immediateFuture(null)); @@ -484,7 +539,12 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 5).toString()); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(JacksonUtil.newObjectNode().put("a", 5).toString()) + .build(); when(telemetryService.saveAndNotify(any(), any(), any(TsKvEntry.class))) .thenReturn(Futures.immediateFuture(null)); @@ -515,7 +575,12 @@ public class TbMathNodeTest { new TbMathResult(TbMathArgumentType.MESSAGE_METADATA, "result", 3, false, false, null), tbMathArgument ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 10).toString()); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(JacksonUtil.newObjectNode().put("a", 10).toString()) + .build(); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); @@ -535,7 +600,12 @@ public class TbMathNodeTest { new TbMathResult(TbMathArgumentType.TIME_SERIES, "result", 3, true, false, DataConstants.SERVER_SCOPE), new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "TestKey") ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 10).toString()); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(JacksonUtil.newObjectNode().put("a", 10).toString()) + .build(); node.onMsg(ctx, msg); ArgumentCaptor tCaptor = ArgumentCaptor.forClass(Throwable.class); @@ -550,7 +620,12 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_ARRAY) + .build(); node.onMsg(ctx, msg); ArgumentCaptor tCaptor = ArgumentCaptor.forClass(Throwable.class); @@ -570,10 +645,20 @@ public class TbMathNodeTest { CountDownLatch slowProcessingLatch = new CountDownLatch(1); List slowMsgList = IntStream.range(0, 5) - .mapToObj(x -> TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originatorSlow, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString())) + .mapToObj(x -> TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originatorSlow) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString()) + .build()) .toList(); List fastMsgList = IntStream.range(0, 2) - .mapToObj(x -> TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originatorFast, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString())) + .mapToObj(x -> TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originatorFast) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString()) + .build()) .toList(); assertThat(slowMsgList.size()).as("slow msgs >= rule-dispatcher pool size").isGreaterThanOrEqualTo(RULE_DISPATCHER_POOL_SIZE); @@ -640,7 +725,12 @@ public class TbMathNodeTest { CountDownLatch slowProcessingLatch = new CountDownLatch(1); List slowMsgList = IntStream.range(0, 5) - .mapToObj(x -> TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originatorSlow, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString())) + .mapToObj(x -> TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originatorSlow) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString()) + .build()) .collect(Collectors.toList()); assertThat(slowMsgList.size()).as("slow msgs >= rule-dispatcher pool size").isGreaterThanOrEqualTo(RULE_DISPATCHER_POOL_SIZE); @@ -713,7 +803,12 @@ public class TbMathNodeTest { }) .toList(); ctxNodes.forEach(ctxNode -> ruleEngineDispatcherExecutor.executeAsync(() -> ctxNode.getRight() - .onMsg(ctxNode.getLeft(), TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, "{\"a\":2,\"b\":2}")))); + .onMsg(ctxNode.getLeft(), TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data("{\"a\":2,\"b\":2}") + .build()))); ctxNodes.forEach(ctxNode -> verify(ctxNode.getRight(), timeout(TIMEOUT)).onMsg(eq(ctxNode.getLeft()), any())); processingLatch.countDown(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeTest.java index 1b2ff4f2bf..c5a0ba4314 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeTest.java @@ -169,7 +169,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { // GIVEN node.init(ctxMock, nodeConfiguration); var msgData = "{\"pulseCounter\": 42}"; - var msg = TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + var msg = TbMsg.builder() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(msgData) + .build(); // WHEN node.onMsg(ctxMock, msg); @@ -184,7 +189,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { public void givenInvalidMsgDataType_whenOnMsg_thenShouldTellNextOther() throws TbNodeException { // GIVEN node.init(ctxMock, nodeConfiguration); - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + var msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_ARRAY) + .build(); // WHEN node.onMsg(ctxMock, msg); @@ -200,7 +210,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { public void givenInputKeyIsNotPresent_whenOnMsg_thenShouldTellNextOther() throws TbNodeException { // GIVEN node.init(ctxMock, nodeConfiguration); - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + var msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); // WHEN node.onMsg(ctxMock, msg); @@ -224,7 +239,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new DoubleDataEntry("temperature", 40.5))); var msgData = "{\"temperature\": 42,\"airPressure\":123}"; - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + var msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(msgData) + .build(); // WHEN node.onMsg(ctxMock, msg); @@ -254,7 +274,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry("temperature", 40L))); var msgData = "{\"temperature\": 42,\"airPressure\":123}"; - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + var msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(msgData) + .build(); // WHEN node.onMsg(ctxMock, msg); @@ -284,7 +309,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry("temperature", "40.0"))); var msgData = "{\"temperature\": 42,\"airPressure\":123}"; - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + var msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(msgData) + .build(); // WHEN node.onMsg(ctxMock, msg); @@ -318,7 +348,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { var msgData = "{\"temperature\": 42,\"airPressure\":123}"; var firstMsgMetaData = new TbMsgMetaData(); firstMsgMetaData.putValue("ts", String.valueOf(3L)); - var firstMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, firstMsgMetaData, msgData); + var firstMsg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(firstMsgMetaData.copy()) + .data(msgData) + .build(); // WHEN node.onMsg(ctxMock, firstMsg); @@ -344,7 +379,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { var secondMsgMetaData = new TbMsgMetaData(); secondMsgMetaData.putValue("ts", String.valueOf(6L)); - var secondMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, secondMsgMetaData, msgData); + var secondMsg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(secondMsgMetaData.copy()) + .data(msgData) + .build(); // WHEN node.onMsg(ctxMock, secondMsg); @@ -375,7 +415,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new DoubleDataEntry("temperature", null))); var msgData = "{\"temperature\": 42,\"airPressure\":123}"; - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + var msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(msgData) + .build(); // WHEN node.onMsg(ctxMock, msg); @@ -403,7 +448,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry("pulseCounter", 200L))); var msgData = "{\"pulseCounter\":\"123\"}"; - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + var msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(msgData) + .build(); // WHEN node.onMsg(ctxMock, msg); @@ -435,7 +485,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry("pulseCounter", 200L))); var msgData = "{\"pulseCounter\":\"123\"}"; - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + var msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(msgData) + .build(); // WHEN node.onMsg(ctxMock, msg); @@ -459,7 +514,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry("pulseCounter", "high"))); var msgData = "{\"pulseCounter\":\"123\"}"; - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + var msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(msgData) + .build(); // WHEN node.onMsg(ctxMock, msg); @@ -484,7 +544,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new BooleanDataEntry("pulseCounter", false))); var msgData = "{\"pulseCounter\":true}"; - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + var msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(msgData) + .build(); // WHEN node.onMsg(ctxMock, msg); @@ -509,7 +574,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new JsonDataEntry("pulseCounter", "{\"isActive\":false}"))); var msgData = "{\"pulseCounter\":{\"isActive\":true}}"; - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + var msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(msgData) + .build(); // WHEN node.onMsg(ctxMock, msg); @@ -552,7 +622,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { List tbMsgList = IntStream.range(0, RULE_DISPATCHER_POOL_SIZE * 2).mapToObj(x -> { var msgData = "{\"pulseCounter\":" + 2 + "}"; - return TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + return TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(msgData) + .build(); }).toList(); CountDownLatch processingLatch = new CountDownLatch(tbMsgList.size()); @@ -597,7 +672,12 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(1L, new DoubleDataEntry("temperature", testConfig.prevValue()))); var msgData = "{\"temperature\":" + testConfig.currentValue() + ",\"airPressure\":123}"; - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + var msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(msgData) + .build(); // WHEN node.onMsg(ctxMock, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNodeTest.java index 5cbe20fc5e..725bb10b12 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNodeTest.java @@ -173,7 +173,13 @@ public class TbFetchDeviceCredentialsNodeTest { final var metaData = new TbMsgMetaData(mdMap); final String data = "{\"TestAttribute_1\": \"humidity\", \"TestAttribute_2\": \"voltage\"}"; - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, metaData, data, callbackMock); + return TbMsg.builder() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .metaData(metaData.copy()) + .data(data) + .callback(callbackMock) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNodeTest.java index fb08dff8e2..84267c772b 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNodeTest.java @@ -17,7 +17,6 @@ package org.thingsboard.rule.engine.metadata; import com.google.common.util.concurrent.Futures; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -247,7 +246,12 @@ public class TbGetAttributesNodeTest extends AbstractRuleNodeUpgradeTest { public void givenFetchLatestTimeseriesToDataAndDataIsNotJsonObject_whenOnMsg_thenException() throws Exception { // GIVEN node = initNode(TbMsgSource.DATA, true, true); - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, ORIGINATOR_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + var msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(ORIGINATOR_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_ARRAY) + .build(); // WHEN var exception = assertThrows(IllegalArgumentException.class, () -> node.onMsg(ctxMock, msg)); @@ -343,7 +347,12 @@ public class TbGetAttributesNodeTest extends AbstractRuleNodeUpgradeTest { msgMetaData.putValue("client_attr_metadata", "client_attr_3"); msgMetaData.putValue("server_attr_metadata", "server_attr_3"); - return TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, entityId, msgMetaData, JacksonUtil.toString(msgData)); + return TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(entityId) + .metaData(msgMetaData.copy()) + .data(JacksonUtil.toString(msgData)) + .build(); } private List getAttributeNames(String prefix) { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java index 894a538c80..6a682679f4 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java @@ -210,7 +210,12 @@ public class TbGetCustomerAttributeNodeTest { public void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException() { // GIVEN node.fetchTo = TbMsgSource.DATA; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_ARRAY) + .build(); // WHEN var exception = assertThrows(IllegalArgumentException.class, () -> node.onMsg(ctxMock, msg)); @@ -225,7 +230,12 @@ public class TbGetCustomerAttributeNodeTest { // GIVEN var userId = new UserId(UUID.randomUUID()); - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, userId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(userId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); when(ctxMock.getTenantId()).thenReturn(TENANT_ID); @@ -469,7 +479,12 @@ public class TbGetCustomerAttributeNodeTest { var msgData = "{\"temp\":42,\"humidity\":77,\"messageBodyPattern1\":\"targetKey2\",\"messageBodyPattern2\":\"sourceKey3\"}"; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, msgMetaData, msgData); + msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .metaData(msgMetaData.copy()) + .data(msgData) + .build(); } @RequiredArgsConstructor diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeTest.java index fa4df10609..0586b84b75 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeTest.java @@ -159,7 +159,12 @@ public class TbGetCustomerDetailsNodeTest { public void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException() { // GIVEN node.fetchTo = TbMsgSource.DATA; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_ARRAY) + .build(); // WHEN var exception = assertThrows(IllegalArgumentException.class, () -> node.onMsg(ctxMock, msg)); @@ -458,7 +463,12 @@ public class TbGetCustomerDetailsNodeTest { var msgData = "{\"dataKey1\":123,\"dataKey2\":\"dataValue2\"}"; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, msgMetaData, msgData); + msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .metaData(msgMetaData.copy()) + .data(msgData) + .build(); } private void mockFindCustomer() { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNodeTest.java index 8cacf33f79..af93181bfb 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNodeTest.java @@ -115,7 +115,12 @@ public class TbGetDeviceAttrNodeTest extends AbstractRuleNodeUpgradeTest { given(deviceServiceMock.findDevicesByQuery(any(TenantId.class), any(DeviceSearchQuery.class))).willReturn(Futures.immediateFuture(Collections.emptyList())); given(ctxMock.getDbCallbackExecutor()).willReturn(executor); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor actualException = ArgumentCaptor.forClass(Throwable.class); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNodeTest.java index fdb73279c0..d67d00416e 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNodeTest.java @@ -135,7 +135,12 @@ public class TbGetOriginatorFieldsNodeTest { public void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException() { // GIVEN node.fetchTo = TbMsgSource.DATA; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_ARRAY) + .build(); // WHEN var exception = assertThrows(IllegalArgumentException.class, () -> node.onMsg(ctxMock, msg)); @@ -164,7 +169,12 @@ public class TbGetOriginatorFieldsNodeTest { node.fetchTo = TbMsgSource.DATA; var msgMetaData = new TbMsgMetaData(); var msgData = "{\"temp\":42,\"humidity\":77}"; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, msgMetaData, msgData); + msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(msgMetaData.copy()) + .data(msgData) + .build(); when(ctxMock.getDeviceService()).thenReturn(deviceServiceMock); when(ctxMock.getTenantId()).thenReturn(DUMMY_TENANT_ID); @@ -206,7 +216,12 @@ public class TbGetOriginatorFieldsNodeTest { node.fetchTo = TbMsgSource.DATA; var msgMetaData = new TbMsgMetaData(); var msgData = "{\"temp\":42,\"humidity\":77}"; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, msgMetaData, msgData); + msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(msgMetaData.copy()) + .data(msgData) + .build(); when(ctxMock.getDeviceService()).thenReturn(deviceServiceMock); when(ctxMock.getTenantId()).thenReturn(DUMMY_TENANT_ID); @@ -249,7 +264,12 @@ public class TbGetOriginatorFieldsNodeTest { "testKey1", "testValue1", "testKey2", "123")); var msgData = "[\"value1\",\"value2\"]"; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, msgMetaData, msgData); + msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(msgMetaData.copy()) + .data(msgData) + .build(); when(ctxMock.getDeviceService()).thenReturn(deviceServiceMock); when(ctxMock.getTenantId()).thenReturn(DUMMY_TENANT_ID); @@ -297,7 +317,12 @@ public class TbGetOriginatorFieldsNodeTest { "testKey1", "testValue1", "testKey2", "123")); var msgData = "[\"value1\",\"value2\"]"; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, msgMetaData, msgData); + msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(msgMetaData.copy()) + .data(msgData) + .build(); when(ctxMock.getDeviceService()).thenReturn(deviceServiceMock); when(ctxMock.getTenantId()).thenReturn(DUMMY_TENANT_ID); @@ -355,7 +380,12 @@ public class TbGetOriginatorFieldsNodeTest { "testKey1", "testValue1", "testKey2", "123")); var msgData = "[\"value1\",\"value2\"]"; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, new DashboardId(UUID.randomUUID()), msgMetaData, msgData); + msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(new DashboardId(UUID.randomUUID())) + .metaData(msgMetaData.copy()) + .data(msgData) + .build(); when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java index 3b6b1eb3c6..f97700e435 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java @@ -223,7 +223,12 @@ public class TbGetRelatedAttributeNodeTest { public void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException() { // GIVEN node.fetchTo = TbMsgSource.DATA; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_ARRAY) + .build(); // WHEN var exception = assertThrows(IllegalArgumentException.class, () -> node.onMsg(ctxMock, msg)); @@ -592,7 +597,12 @@ public class TbGetRelatedAttributeNodeTest { msgData = "{\"temp\":42,\"humidity\":77,\"messageBodyPattern1\":\"targetKey2\",\"messageBodyPattern2\":\"sourceKey3\"}"; } - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, msgMetaData, msgData); + msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .metaData(msgMetaData.copy()) + .data(msgData) + .build(); } @RequiredArgsConstructor diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNodeTest.java index 87f273df93..c27f5450f3 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNodeTest.java @@ -182,7 +182,12 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { // WHEN-THEN node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); assertThatThrownBy(() -> node.onMsg(ctxMock, msg)) .isInstanceOf(RuntimeException.class) .hasMessage("Interval start should be less than Interval end"); @@ -205,7 +210,12 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { long endTs = 1719220353000L; TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("mdStartInterval", String.valueOf(startTs)); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, "{\"msgEndInterval\":\"" + endTs + "\"}"); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(metaData.copy()) + .data("{\"msgEndInterval\":\"" + endTs + "\"}") + .build(); node.onMsg(ctxMock, msg); // THEN @@ -227,7 +237,12 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { given(timeseriesServiceMock.findAll(any(TenantId.class), any(EntityId.class), anyList())).willReturn(Futures.immediateFuture(Collections.emptyList())); // WHEN - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); // THEN @@ -251,7 +266,12 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { // WHEN TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("mdTsKey", "humidity"); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, "{\"msgTsKey\":\"pressure\"}"); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(metaData.copy()) + .data("{\"msgTsKey\":\"pressure\"}") + .build(); node.onMsg(ctxMock, msg); // THEN @@ -276,7 +296,12 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { given(timeseriesServiceMock.findAll(any(TenantId.class), any(EntityId.class), anyList())).willReturn(Futures.immediateFuture(Collections.emptyList())); // WHEN - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); // THEN @@ -306,7 +331,12 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { given(timeseriesServiceMock.findAll(any(TenantId.class), any(EntityId.class), anyList())).willReturn(Futures.immediateFuture(Collections.emptyList())); // WHEN - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); // THEN @@ -346,7 +376,12 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { given(timeseriesServiceMock.findAll(any(TenantId.class), any(EntityId.class), anyList())).willReturn(Futures.immediateFuture(Collections.emptyList())); // WHEN - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); // THEN @@ -382,7 +417,12 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); // WHEN-THEN - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, "{\"msgStartInterval\":\"start\"}"); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data("{\"msgStartInterval\":\"start\"}") + .build(); assertThatThrownBy(() -> node.onMsg(ctxMock, msg)).isInstanceOf(IllegalArgumentException.class).hasMessage(errorMsg); } @@ -411,7 +451,12 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { given(timeseriesServiceMock.findAll(any(TenantId.class), any(EntityId.class), anyList())).willReturn(Futures.immediateFuture(tsKvEntries)); // WHEN - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); // THEN @@ -442,7 +487,12 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { given(timeseriesServiceMock.findAll(any(TenantId.class), any(EntityId.class), anyList())).willReturn(Futures.immediateFuture(tsKvEntries)); // WHEN - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); // THEN diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java index 9f20301b46..a7ba3f54ed 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java @@ -190,7 +190,12 @@ public class TbGetTenantAttributeNodeTest { public void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException() { // GIVEN node.fetchTo = TbMsgSource.DATA; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_ARRAY) + .build(); // WHEN var exception = assertThrows(IllegalArgumentException.class, () -> node.onMsg(ctxMock, msg)); @@ -398,7 +403,12 @@ public class TbGetTenantAttributeNodeTest { var msgData = "{\"temp\":42,\"humidity\":77,\"messageBodyPattern1\":\"targetKey2\",\"messageBodyPattern2\":\"sourceKey3\"}"; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, msgMetaData, msgData); + msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .metaData(msgMetaData.copy()) + .data(msgData) + .build(); } @RequiredArgsConstructor diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeTest.java index 6649cf49de..b030ae75aa 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeTest.java @@ -129,7 +129,12 @@ public class TbGetTenantDetailsNodeTest { public void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException() { // GIVEN node.fetchTo = TbMsgSource.DATA; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_ARRAY) + .build(); // WHEN var exception = assertThrows(IllegalArgumentException.class, () -> node.onMsg(ctxMock, msg)); @@ -289,7 +294,12 @@ public class TbGetTenantDetailsNodeTest { var msgData = "{\"dataKey1\":123,\"dataKey2\":\"dataValue2\"}"; - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, msgMetaData, msgData); + msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DUMMY_DEVICE_ORIGINATOR) + .metaData(msgMetaData.copy()) + .data(msgData) + .build(); } private void mockFindTenant() { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeTest.java index 8fa8c387fd..60c6eebb5b 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeTest.java @@ -283,7 +283,12 @@ public class TbMqttNodeTest extends AbstractRuleNodeUpgradeTest { return null; }).given(future).addListener(any()); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, data); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(metaData.copy()) + .data(data) + .build(); mqttNode.onMsg(ctxMock, msg); then(ctxMock).should().ack(msg); @@ -322,7 +327,12 @@ public class TbMqttNodeTest extends AbstractRuleNodeUpgradeTest { return null; }).given(future).addListener(any()); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, "\"string\""); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data("\"string\"") + .build(); mqttNode.onMsg(ctxMock, msg); then(ctxMock).should(never()).ack(msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/DeviceStateTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/DeviceStateTest.java index 5cee1aa5b3..973a426513 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/DeviceStateTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/DeviceStateTest.java @@ -95,7 +95,12 @@ public class DeviceStateTest { when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), any())).thenAnswer(invocationOnMock -> { TbMsgType type = invocationOnMock.getArgument(1); String data = invocationOnMock.getArgument(invocationOnMock.getArguments().length - 1); - return TbMsg.newMsg(type, null, TbMsgMetaData.EMPTY, data); + return TbMsg.builder() + .type(type) + .originator(null) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(data) + .build(); }); } @@ -107,8 +112,12 @@ public class DeviceStateTest { DeviceId deviceId = new DeviceId(UUID.randomUUID()); DeviceState deviceState = createDeviceState(deviceId, alarmConfig); - TbMsg attributeUpdateMsg = TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, - deviceId, TbMsgMetaData.EMPTY, "{ \"enabled\": false }"); + TbMsg attributeUpdateMsg = TbMsg.builder() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data("{ \"enabled\": false }") + .build(); deviceState.process(ctx, attributeUpdateMsg); @@ -116,11 +125,21 @@ public class DeviceStateTest { verify(ctx).enqueueForTellNext(resultMsgCaptor.capture(), eq("Alarm Created")); Alarm alarm = JacksonUtil.fromString(resultMsgCaptor.getValue().getData(), Alarm.class); - deviceState.process(ctx, TbMsg.newMsg(TbMsgType.ALARM_CLEAR, deviceId, TbMsgMetaData.EMPTY, JacksonUtil.toString(alarm))); + deviceState.process(ctx, TbMsg.builder() + .type(TbMsgType.ALARM_CLEAR) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(JacksonUtil.toString(alarm)) + .build()); reset(ctx); String deletedAttributes = "{ \"attributes\": [ \"other\" ] }"; - deviceState.process(ctx, TbMsg.newMsg(TbMsgType.ATTRIBUTES_DELETED, deviceId, TbMsgMetaData.EMPTY, deletedAttributes)); + deviceState.process(ctx, TbMsg.builder() + .type(TbMsgType.ATTRIBUTES_DELETED) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(deletedAttributes) + .build()); verify(ctx, never()).enqueueForTellNext(any(), anyString()); } @@ -130,17 +149,31 @@ public class DeviceStateTest { DeviceId deviceId = new DeviceId(UUID.randomUUID()); DeviceState deviceState = createDeviceState(deviceId, alarmConfig); - TbMsg attributeUpdateMsg = TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, - deviceId, TbMsgMetaData.EMPTY, "{ \"enabled\": false }"); + TbMsg attributeUpdateMsg = TbMsg.builder() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data("{ \"enabled\": false }") + .build(); deviceState.process(ctx, attributeUpdateMsg); ArgumentCaptor resultMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); verify(ctx).enqueueForTellNext(resultMsgCaptor.capture(), eq("Alarm Created")); Alarm alarm = JacksonUtil.fromString(resultMsgCaptor.getValue().getData(), Alarm.class); - deviceState.process(ctx, TbMsg.newMsg(TbMsgType.ALARM_CLEAR, deviceId, TbMsgMetaData.EMPTY, JacksonUtil.toString(alarm))); - - TbMsg alarmDeleteNotification = TbMsg.newMsg(TbMsgType.ALARM_DELETE, deviceId, TbMsgMetaData.EMPTY, JacksonUtil.toString(alarm)); + deviceState.process(ctx, TbMsg.builder() + .type(TbMsgType.ALARM_CLEAR) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(JacksonUtil.toString(alarm)) + .build()); + + TbMsg alarmDeleteNotification = TbMsg.builder() + .type(TbMsgType.ALARM_DELETE) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(JacksonUtil.toString(alarm)) + .build(); assertDoesNotThrow(() -> { deviceState.process(ctx, alarmDeleteNotification); }); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java index 04371c23a0..afc5401d89 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java @@ -127,8 +127,13 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 42); - TbMsg msg = TbMsg.newMsg("123456789", deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data)); + TbMsg msg = TbMsg.builder() + .type("123456789") + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); @@ -146,8 +151,15 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 42); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); @@ -198,26 +210,50 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")).thenReturn(null); registerCreateAlarmMock(alarmService.createAlarm(any()), true); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.builder() + .type(TbMsgType.ALARM) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())).thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 42); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); verify(ctx).enqueueForTellNext(theMsg, "Alarm Created"); verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); - TbMsg theMsg2 = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, "2"); + TbMsg theMsg2 = TbMsg.builder() + .type(TbMsgType.ALARM) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data("2") + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())).thenReturn(theMsg2); registerCreateAlarmMock(alarmService.updateAlarm(any()), false); Thread.sleep(1); - TbMsg msg2 = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg2 = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg2); verify(ctx).tellSuccess(msg2); verify(ctx).enqueueForTellNext(theMsg2, "Alarm Updated"); @@ -274,19 +310,36 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm1")).thenReturn(null); registerCreateAlarmMock(alarmService.createAlarm(any()), true); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.builder() + .type(TbMsgType.ALARM) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())).thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 42); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); verify(ctx).enqueueForTellNext(theMsg, "Alarm Created"); verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); - TbMsg theMsg2 = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg2 = TbMsg.builder() + .type(TbMsgType.ALARM) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())).thenReturn(theMsg2); AlarmInfo alarm = new AlarmInfo(new Alarm(new AlarmId(UUID.randomUUID()))); @@ -305,8 +358,15 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { when(alarmService.updateAlarm(any())).thenReturn(result); data.put("temperature", 52); - TbMsg msg2 = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg2 = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg2); verify(ctx).tellSuccess(msg2); verify(ctx).enqueueForTellNext(theMsg2, "Alarm Severity Updated"); @@ -380,14 +440,26 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(attrListListenableFuture); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.builder() + .type(TbMsgType.ALARM) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); Mockito.when(ctx.newMsg(Mockito.any(), Mockito.any(TbMsgType.class), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 21); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -468,14 +540,26 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), Mockito.any(AttributeScope.class), Mockito.anyString())) .thenReturn(attrListListenableFuture); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.builder() + .type(TbMsgType.ALARM) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 21); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -538,14 +622,26 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listListenableFutureWithLess); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.builder() + .type(TbMsgType.ALARM) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 35); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -634,14 +730,26 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listListenableFuture); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.builder() + .type(TbMsgType.ALARM) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 35); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -655,8 +763,15 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Thread.sleep(halfOfAlarmDelay + 1); - TbMsg msg2 = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg2 = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg2); verify(ctx).tellSuccess(msg2); @@ -760,14 +875,26 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listNoDurationAttribute); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.builder() + .type(TbMsgType.ALARM) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 150); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -781,8 +908,15 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Thread.sleep(halfOfAlarmDelay + 1); - TbMsg msg2 = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg2 = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg2); verify(ctx).tellSuccess(msg2); @@ -871,14 +1005,26 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listListenableFuture); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.builder() + .type(TbMsgType.ALARM) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 150); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -886,8 +1032,15 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { verify(ctx, Mockito.never()).tellNext(theMsg, "Alarm Created"); data.put("temperature", 151); - TbMsg msg2 = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg2 = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg2); verify(ctx).tellSuccess(msg2); @@ -989,14 +1142,26 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listNoDurationAttribute); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.builder() + .type(TbMsgType.ALARM) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 150); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -1004,8 +1169,15 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { verify(ctx, Mockito.never()).tellNext(theMsg, "Alarm Created"); data.put("temperature", 151); - TbMsg msg2 = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg2 = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg2); verify(ctx).tellSuccess(msg2); @@ -1086,14 +1258,26 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listListenableFuture); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.builder() + .type(TbMsgType.ALARM) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 35); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -1107,8 +1291,15 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Thread.sleep(halfOfAlarmDelay + 1); - TbMsg msg2 = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg2 = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg2); verify(ctx).tellSuccess(msg2); @@ -1185,14 +1376,26 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listListenableFuture); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.builder() + .type(TbMsgType.ALARM) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 35); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -1268,14 +1471,26 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listListenableFutureActiveSchedule); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.builder() + .type(TbMsgType.ALARM) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 35); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .ruleChainId(null) + .ruleNodeId(null) + .build(); // Mockito.reset(ctx); @@ -1365,12 +1580,24 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listListenableFutureInactiveSchedule); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.builder() + .type(TbMsgType.ALARM) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 35); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -1444,14 +1671,26 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(customerId), eq(AttributeScope.SERVER_SCOPE), Mockito.anyString())) .thenReturn(optionalListenableFutureWithLess); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.builder() + .type(TbMsgType.ALARM) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 25); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -1518,14 +1757,26 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(AttributeScope.SERVER_SCOPE), Mockito.anyString())) .thenReturn(optionalListenableFutureWithLess); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.builder() + .type(TbMsgType.ALARM) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 40); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -1602,14 +1853,26 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(AttributeScope.SERVER_SCOPE), Mockito.anyString())) .thenReturn(optionalListenableFutureWithLess); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.builder() + .type(TbMsgType.ALARM) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 150L); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -1688,14 +1951,26 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(AttributeScope.SERVER_SCOPE), Mockito.anyString())) .thenReturn(optionalListenableFutureWithLess); - TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + TbMsg theMsg = TbMsg.builder() + .type(TbMsgType.ALARM) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 150L); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java index 8ce71f684c..91a9055c37 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java @@ -147,7 +147,12 @@ public class TbRabbitMqNodeTest { given(ctxMock.getExternalCallExecutor()).willReturn(executor); node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, data); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(metaData.copy()) + .data(data) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should().ack(msg); @@ -178,7 +183,12 @@ public class TbRabbitMqNodeTest { given(ctxMock.getExternalCallExecutor()).willReturn(executor); node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should(never()).ack(any(TbMsg.class)); @@ -201,7 +211,12 @@ public class TbRabbitMqNodeTest { node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); TbMsgMetaData metaData = new TbMsgMetaData(); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(metaData.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should(forceAck ? times(1) : never()).ack(any(TbMsg.class)); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbHttpClientTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbHttpClientTest.java index 2eb65c1787..cc883db254 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbHttpClientTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbHttpClientTest.java @@ -140,11 +140,18 @@ public class TbHttpClientTest { var httpClient = new TbHttpClient(config, eventLoop); - var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, new DeviceId(EntityId.NULL_UUID), TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); - var successMsg = TbMsg.newMsg( - TbMsgType.POST_TELEMETRY_REQUEST, msg.getOriginator(), - msg.getMetaData(), msg.getData() - ); + var msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(new DeviceId(EntityId.NULL_UUID)) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); + var successMsg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(msg.getOriginator()) + .metaData(msg.getMetaData().copy()) + .data(msg.getData()) + .build(); var ctx = mock(TbContext.class); when(ctx.transformMsg( diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java index e7d541d587..9f19fe019b 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java @@ -142,7 +142,15 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest { config.setRestEndpointUrlPattern(String.format("http://localhost:%d%s", server.getLocalPort(), path)); initWithConfig(config); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .metaData(metaData.copy()) + .dataType(TbMsgDataType.JSON) + .data(TbMsg.EMPTY_JSON_OBJECT) + .ruleChainId(ruleChainId) + .ruleNodeId(ruleNodeId) + .build(); restNode.onMsg(ctx, msg); assertTrue(latch.await(10, TimeUnit.SECONDS), "Server handled request"); @@ -203,7 +211,15 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest { config.setRestEndpointUrlPattern(String.format("http://localhost:%d%s", server.getLocalPort(), path)); initWithConfig(config); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(originator) + .metaData(metaData.copy()) + .dataType(TbMsgDataType.JSON) + .data(TbMsg.EMPTY_JSON_OBJECT) + .ruleChainId(ruleChainId) + .ruleNodeId(ruleNodeId) + .build(); restNode.onMsg(ctx, msg); assertTrue(latch.await(10, TimeUnit.SECONDS), "Server handled request"); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbSendRestApiCallReplyNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbSendRestApiCallReplyNodeTest.java index c603e111e2..7ca88da0a3 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbSendRestApiCallReplyNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbSendRestApiCallReplyNodeTest.java @@ -89,7 +89,12 @@ public class TbSendRestApiCallReplyNodeTest { Map metadata = Map.of( requestIdAttribute, requestUUIDStr, serviceIdAttribute, serviceIdStr); - TbMsg msg = TbMsg.newMsg(TbMsgType.REST_API_REQUEST, DEVICE_ID, new TbMsgMetaData(metadata), data); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.REST_API_REQUEST) + .originator(DEVICE_ID) + .metaData(new TbMsgMetaData(metadata).copy()) + .data(data) + .build(); node.onMsg(ctxMock, msg); @@ -109,7 +114,12 @@ public class TbSendRestApiCallReplyNodeTest { @ParameterizedTest @MethodSource public void givenInvalidRequest_whenOnMsg_thenTellFailure(TbMsgMetaData metaData, String data, String errorMsg) { - TbMsg msg = TbMsg.newMsg(TbMsgType.REST_API_REQUEST, DEVICE_ID, metaData, data); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.REST_API_REQUEST) + .originator(DEVICE_ID) + .metaData(metaData.copy()) + .data(data) + .build(); node.onMsg(ctxMock, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java index 9cab33fcdd..c5bab86d8c 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java @@ -92,8 +92,15 @@ public class TbSendRPCReplyNodeTest { public void sendReplyToTransport() { when(ctx.getRpcService()).thenReturn(rpcService); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, getDefaultMetadata(), - TbMsgDataType.JSON, DUMMY_DATA, null, null); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(getDefaultMetadata().copy()) + .dataType(TbMsgDataType.JSON) + .data(DUMMY_DATA) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg); @@ -111,8 +118,15 @@ public class TbSendRPCReplyNodeTest { TbMsgMetaData defaultMetadata = getDefaultMetadata(); defaultMetadata.putValue(DataConstants.EDGE_ID, UUID.randomUUID().toString()); defaultMetadata.putValue(DataConstants.DEVICE_ID, UUID.randomUUID().toString()); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, defaultMetadata, - TbMsgDataType.JSON, DUMMY_DATA, null, null); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(defaultMetadata.copy()) + .dataType(TbMsgDataType.JSON) + .data(DUMMY_DATA) + .ruleChainId(null) + .ruleNodeId(null) + .build(); node.onMsg(ctx, msg); @@ -124,7 +138,12 @@ public class TbSendRPCReplyNodeTest { @EnumSource(EntityType.class) public void testOriginatorEntityTypes(EntityType entityType) { EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, "0f386739-210f-4e23-8739-23f84a172adc"); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(entityId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctx, msg); @@ -138,7 +157,12 @@ public class TbSendRPCReplyNodeTest { @ParameterizedTest @MethodSource public void testForAvailabilityOfMetadataAndDataValues(TbMsgMetaData metaData, String errorMsg) { - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, metaData, TbMsg.EMPTY_STRING); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(metaData.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); node.onMsg(ctx, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java index e9d25e0d5e..322d334c31 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java @@ -107,7 +107,12 @@ public class TbSendRPCRequestNodeTest { TbMsgMetaData msgMetadata = new TbMsgMetaData(); msgMetadata.putValue("oneway", mdKeyValue); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, msgMetadata, MSG_DATA); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .metaData(msgMetadata.copy()) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); var ruleEngineDeviceRpcRequestCaptor = captureRequest(); @@ -128,7 +133,12 @@ public class TbSendRPCRequestNodeTest { given(ctxMock.getRpcService()).willReturn(rpcServiceMock); given(ctxMock.getTenantId()).willReturn(TENANT_ID); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, TbMsgMetaData.EMPTY, MSG_DATA); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(RuleEngineDeviceRpcRequest.class); @@ -149,7 +159,12 @@ public class TbSendRPCRequestNodeTest { given(ctxMock.getRpcService()).willReturn(rpcServiceMock); given(ctxMock.getTenantId()).willReturn(TENANT_ID); - TbMsg msg = TbMsg.newMsg(TbMsgType.TO_SERVER_RPC_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, MSG_DATA); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.TO_SERVER_RPC_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor requestCaptor = captureRequest(); @@ -170,7 +185,12 @@ public class TbSendRPCRequestNodeTest { "requestId": 12345 } """; - TbMsg msg = TbMsg.newMsg(TbMsgType.TO_SERVER_RPC_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, data); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.TO_SERVER_RPC_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(data) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor requestCaptor = captureRequest(); @@ -185,7 +205,12 @@ public class TbSendRPCRequestNodeTest { String requestUUID = "b795a241-5a30-48fb-92d5-46b864d47130"; TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue("requestUUID", requestUUID); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .metaData(metadata.copy()) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor requestCaptor = captureRequest(); @@ -200,7 +225,12 @@ public class TbSendRPCRequestNodeTest { TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue("requestUUID", requestUUID); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .metaData(metadata.copy()) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor requestCaptor = captureRequest(); @@ -215,7 +245,12 @@ public class TbSendRPCRequestNodeTest { String originServiceId = "service-id-123"; TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue("originServiceId", originServiceId); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .metaData(metadata.copy()) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor requestCaptor = captureRequest(); @@ -230,7 +265,12 @@ public class TbSendRPCRequestNodeTest { TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue("originServiceId", originServiceId); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .metaData(metadata.copy()) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor requestCaptor = captureRequest(); @@ -245,7 +285,12 @@ public class TbSendRPCRequestNodeTest { String expirationTime = "2000000000000"; TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue(DataConstants.EXPIRATION_TIME, expirationTime); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .metaData(metadata.copy()) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor requestCaptor = captureRequest(); @@ -260,7 +305,12 @@ public class TbSendRPCRequestNodeTest { TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue(DataConstants.EXPIRATION_TIME, expirationTime); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .metaData(metadata.copy()) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor requestCaptor = captureRequest(); @@ -275,7 +325,12 @@ public class TbSendRPCRequestNodeTest { Integer retries = 3; TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue(DataConstants.RETRIES, String.valueOf(retries)); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .metaData(metadata.copy()) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor requestCaptor = captureRequest(); @@ -290,7 +345,12 @@ public class TbSendRPCRequestNodeTest { TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue(DataConstants.RETRIES, retries); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .metaData(metadata.copy()) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor requestCaptor = captureRequest(); @@ -303,7 +363,12 @@ public class TbSendRPCRequestNodeTest { given(ctxMock.getRpcService()).willReturn(rpcServiceMock); given(ctxMock.getTenantId()).willReturn(TENANT_ID); - TbMsg msg = TbMsg.newMsg(msgType, DEVICE_ID, TbMsgMetaData.EMPTY, MSG_DATA); + TbMsg msg = TbMsg.builder() + .type(msgType) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor requestCaptor = captureRequest(); @@ -322,7 +387,12 @@ public class TbSendRPCRequestNodeTest { TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue(DataConstants.PERSISTENT, isPersisted); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .metaData(metadata.copy()) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor requestCaptor = captureRequest(); @@ -346,7 +416,12 @@ public class TbSendRPCRequestNodeTest { @Test public void givenRpcResponseWithoutError_whenOnMsg_thenSendsRpcRequest() { - TbMsg outMsg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg outMsg = TbMsg.builder() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); given(ctxMock.getRpcService()).willReturn(rpcServiceMock); given(ctxMock.getTenantId()).willReturn(TENANT_ID); @@ -361,7 +436,12 @@ public class TbSendRPCRequestNodeTest { return null; }).given(rpcServiceMock).sendRpcRequestToDevice(any(RuleEngineDeviceRpcRequest.class), any(Consumer.class)); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, TbMsgMetaData.EMPTY, MSG_DATA); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should().enqueueForTellNext(outMsg, TbNodeConnectionType.SUCCESS); @@ -370,7 +450,12 @@ public class TbSendRPCRequestNodeTest { @Test public void givenRpcResponseWithError_whenOnMsg_thenTellFailure() { - TbMsg outMsg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg outMsg = TbMsg.builder() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); given(ctxMock.getRpcService()).willReturn(rpcServiceMock); given(ctxMock.getTenantId()).willReturn(TENANT_ID); @@ -384,7 +469,12 @@ public class TbSendRPCRequestNodeTest { return null; }).given(rpcServiceMock).sendRpcRequestToDevice(any(RuleEngineDeviceRpcRequest.class), any(Consumer.class)); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, TbMsgMetaData.EMPTY, MSG_DATA); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(MSG_DATA) + .build(); node.onMsg(ctxMock, msg); then(ctxMock).should().enqueueForTellFailure(outMsg, RpcError.NO_ACTIVE_CONNECTION.name()); @@ -396,7 +486,12 @@ public class TbSendRPCRequestNodeTest { public void givenOriginatorIsNotDevice_whenOnMsg_thenThrowsException(EntityType entityType) { EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, "ac21a1bb-eabf-4463-8313-24bea1f498d9"); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(entityId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor throwableCaptor = ArgumentCaptor.forClass(Throwable.class); @@ -409,7 +504,12 @@ public class TbSendRPCRequestNodeTest { @ParameterizedTest @ValueSource(strings = {"method", "params"}) public void givenMethodOrParamsAreNotPresent_whenOnMsg_thenThrowsException(String key) { - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, "{\"" + key + "\": \"value\"}"); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data("{\"" + key + "\": \"value\"}") + .build(); node.onMsg(ctxMock, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java index b5cf5533b8..4e1add54fe 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java @@ -164,7 +164,12 @@ class TbMsgAttributesNodeTest extends AbstractRuleNodeUpgradeTest { md.putValue(NOTIFY_DEVICE_METADATA_KEY, mdValue); } // dummy list with one ts kv to pass the empty list check. - var testTbMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, md, TbMsg.EMPTY_STRING); + var testTbMsg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(md.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); List testAttrList = List.of(new BaseAttributeKvEntry(0L, new StringDataEntry("testKey", "testValue"))); node.saveAttr(testAttrList, ctxMock, testTbMsg, AttributeScope.SHARED_SCOPE, false); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNodeTest.java index 3d546c80f7..680e8b5004 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNodeTest.java @@ -139,7 +139,13 @@ public class TbMsgDeleteAttributesNodeTest { } final String data = "{\"TestAttribute_2\": \"humidity\", \"TestAttribute_3\": \"voltage\"}"; - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, deviceId, metaData, data, callback); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(deviceId) + .metaData(metaData.copy()) + .data(data) + .callback(callback) + .build(); node.onMsg(ctx, msg); ArgumentCaptor successCaptor = ArgumentCaptor.forClass(Runnable.class); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java index 1fb4e9eefd..0a7cbc7307 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java @@ -96,7 +96,12 @@ public class TbMsgTimeseriesNodeTest { @EnumSource(TbMsgType.class) public void givenMsgTypeAndEmptyMsgData_whenOnMsg_thenVerifyFailureMsg(TbMsgType msgType) throws TbNodeException { init(); - TbMsg msg = TbMsg.newMsg(msgType, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + TbMsg msg = TbMsg.builder() + .type(msgType) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_ARRAY) + .build(); node.onMsg(ctxMock, msg); ArgumentCaptor throwableCaptor = ArgumentCaptor.forClass(Throwable.class); @@ -122,7 +127,12 @@ public class TbMsgTimeseriesNodeTest { "humidity": 77 } """; - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, data); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(data) + .build(); when(ctxMock.getTelemetryService()).thenReturn(telemetryServiceMock); when(ctxMock.getTenantId()).thenReturn(TENANT_ID); @@ -159,7 +169,12 @@ public class TbMsgTimeseriesNodeTest { """; long ts = System.currentTimeMillis(); var metadata = Map.of("ts", String.valueOf(ts)); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, new TbMsgMetaData(metadata), data); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(new TbMsgMetaData(metadata).copy()) + .data(data) + .build(); when(ctxMock.getTelemetryService()).thenReturn(telemetryServiceMock); when(ctxMock.getTenantId()).thenReturn(TENANT_ID); @@ -197,7 +212,12 @@ public class TbMsgTimeseriesNodeTest { """; var metadata = new TbMsgMetaData(); metadata.putValue("TTL", ttlFromMd); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metadata, data); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(metadata.copy()) + .data(data) + .build(); node.onMsg(ctxMock, msg); verify(telemetryServiceMock).saveAndNotify(eq(TENANT_ID), isNull(), eq(DEVICE_ID), anyList(), eq(expectedTtl), any(TelemetryNodeCallback.class)); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java index 098fe3efda..dd0b3e3385 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java @@ -176,7 +176,12 @@ public class TbChangeOriginatorNodeTest { Device device = new Device(DEVICE_ID); device.setCustomerId(CUSTOMER_ID); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); TbMsg expectedMsg = TbMsg.transformMsgOriginator(msg, CUSTOMER_ID); given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); @@ -199,7 +204,12 @@ public class TbChangeOriginatorNodeTest { public void givenOriginatorSourceIsTenant_whenOnMsg_thenTellSuccess() throws TbNodeException { config.setOriginatorSource(TENANT); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, ASSET_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(ASSET_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); TbMsg expectedMsg = TbMsg.transformMsgOriginator(msg, TENANT_ID); given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); @@ -219,7 +229,12 @@ public class TbChangeOriginatorNodeTest { public void givenOriginatorSourceIsRelatedAndNewOriginatorIsNull_whenOnMsg_thenTellFailure() throws TbNodeException { config.setOriginatorSource(RELATED); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, ASSET_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(ASSET_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); given(ctxMock.getRelationService()).willReturn(relationServiceMock); @@ -253,7 +268,12 @@ public class TbChangeOriginatorNodeTest { Alarm alarm = new Alarm(alarmId); alarm.setOriginator(DEVICE_ID); - TbMsg msg = TbMsg.newMsg(TbMsgType.ALARM, alarmId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.ALARM) + .originator(alarmId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); TbMsg expectedMsg = TbMsg.transformMsgOriginator(msg, DEVICE_ID); given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); @@ -279,7 +299,12 @@ public class TbChangeOriginatorNodeTest { config.setEntityType(EntityType.ASSET.name()); config.setEntityNamePattern(entityNamePattern); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, data); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(metaData.copy()) + .data(data) + .build(); TbMsg expectedMsg = TbMsg.transformMsgOriginator(msg, ASSET_ID); given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); @@ -315,7 +340,12 @@ public class TbChangeOriginatorNodeTest { TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("md-name-pattern", "test-asset"); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .metaData(metaData.copy()) + .data(TbMsg.EMPTY_JSON_OBJECT) + .build(); given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); given(ctxMock.getAssetService()).willReturn(assetServiceMock); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeTest.java index b680981e34..fe27c4452b 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeTest.java @@ -196,7 +196,13 @@ public class TbCopyKeysNodeTest { "voltageDataValue", "220", "city", "NY" ); - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, new TbMsgMetaData(mdMap), data, callback); + return TbMsg.builder() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .metaData(new TbMsgMetaData(mdMap).copy()) + .data(data) + .callback(callback) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNodeTest.java index 670b117a2c..c47beffdec 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNodeTest.java @@ -173,7 +173,13 @@ public class TbDeleteKeysNodeTest { "voltageDataValue", "220", "city", "NY" ); - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, new TbMsgMetaData(mdMap), data, callback); + return TbMsg.builder() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .metaData(new TbMsgMetaData(mdMap).copy()) + .data(data) + .callback(callback) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbJsonPathNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbJsonPathNodeTest.java index 2c09bf555c..3ca1521f43 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbJsonPathNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbJsonPathNodeTest.java @@ -171,6 +171,12 @@ public class TbJsonPathNodeTest { Map mdMap = Map.of("country", "US", "city", "NY" ); - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, new TbMsgMetaData(mdMap), data, callback); + return TbMsg.builder() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .metaData(new TbMsgMetaData(mdMap).copy()) + .data(data) + .callback(callback) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbMsgDeduplicationNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbMsgDeduplicationNodeTest.java index 26fd9081a9..1a79865867 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbMsgDeduplicationNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbMsgDeduplicationNodeTest.java @@ -103,7 +103,12 @@ public class TbMsgDeduplicationNodeTest extends AbstractRuleNodeUpgradeTest { EntityId originator = (EntityId) (invocationOnMock.getArguments())[2]; TbMsgMetaData metaData = (TbMsgMetaData) (invocationOnMock.getArguments())[3]; String data = (String) (invocationOnMock.getArguments())[4]; - return TbMsg.newMsg(type, originator, metaData.copy(), data); + return TbMsg.builder() + .type(type) + .originator(originator) + .metaData(metaData.copy().copy()) + .data(data) + .build(); }).when(ctx).newMsg(isNull(), eq(TbMsgType.DEDUPLICATION_TIMEOUT_SELF_MSG), nullable(EntityId.class), any(TbMsgMetaData.class), any(String.class)); node = spy(new TbMsgDeduplicationNode()); config = new TbMsgDeduplicationNodeConfiguration().defaultConfiguration(); @@ -452,12 +457,13 @@ public class TbMsgDeduplicationNodeTest extends AbstractRuleNodeUpgradeTest { dataNode.put("deviceId", deviceId.getId().toString()); TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("ts", String.valueOf(ts)); - return TbMsg.newMsg( - DataConstants.MAIN_QUEUE_NAME, - TbMsgType.POST_TELEMETRY_REQUEST, - deviceId, - metaData, - JacksonUtil.toString(dataNode)); + return TbMsg.builder() + .queueName(DataConstants.MAIN_QUEUE_NAME) + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(deviceId) + .metaData(metaData.copy()) + .data(JacksonUtil.toString(dataNode)) + .build(); } private String getMergedData(List msgs) { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeTest.java index 03580920ce..851d45edc7 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeTest.java @@ -188,6 +188,12 @@ public class TbRenameKeysNodeTest { "country", "US", "city", "NY" ); - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, new TbMsgMetaData(mdMap), data, callback); + return TbMsg.builder() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .metaData(new TbMsgMetaData(mdMap).copy()) + .data(data) + .callback(callback) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNodeTest.java index 8abf83fa48..1ba7760244 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNodeTest.java @@ -132,6 +132,12 @@ public class TbSplitArrayMsgNodeTest { "country", "US", "city", "NY" ); - return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, new TbMsgMetaData(mdMap), data, callback); + return TbMsg.builder() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(entityId) + .metaData(new TbMsgMetaData(mdMap).copy()) + .data(data) + .callback(callback) + .build(); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java index 98ecc237b6..5c8ea2c0f3 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java @@ -61,8 +61,24 @@ public class TbTransformMsgNodeTest { RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased()); RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, null, metaData, TbMsgDataType.JSON,rawJson, ruleChainId, ruleNodeId); - TbMsg transformedMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, null, metaData, TbMsgDataType.JSON, "{new}", ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(null) + .metaData(metaData.copy()) + .dataType(TbMsgDataType.JSON) + .data(rawJson) + .ruleChainId(ruleChainId) + .ruleNodeId(ruleNodeId) + .build(); + TbMsg transformedMsg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(null) + .metaData(metaData.copy()) + .dataType(TbMsgDataType.JSON) + .data("{new}") + .ruleChainId(ruleChainId) + .ruleNodeId(ruleNodeId) + .build(); when(scriptEngine.executeUpdateAsync(msg)).thenReturn(Futures.immediateFuture(Collections.singletonList(transformedMsg))); node.onMsg(ctx, msg); @@ -81,7 +97,15 @@ public class TbTransformMsgNodeTest { RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased()); RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, null, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(null) + .metaData(metaData.copy()) + .dataType(TbMsgDataType.JSON) + .data(rawJson) + .ruleChainId(ruleChainId) + .ruleNodeId(ruleNodeId) + .build(); when(scriptEngine.executeUpdateAsync(msg)).thenReturn(Futures.immediateFailedFuture(new IllegalStateException("error"))); node.onMsg(ctx, msg); From e4cc2b4f9ff1b6b5810d4069966db96b48f2b41b Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 13 Dec 2024 13:23:15 +0200 Subject: [PATCH 008/108] Introduce TbMsgTransformer --- .../actors/ruleChain/DefaultTbContext.java | 8 +- .../server/controller/RpcV2Controller.java | 2 +- .../controller/RuleChainController.java | 2 +- .../controller/RuleEngineController.java | 2 +- .../service/action/EntityActionService.java | 2 +- .../device/DeviceProvisionServiceImpl.java | 4 +- .../service/edge/rpc/EdgeGrpcService.java | 2 +- .../edge/rpc/processor/BaseEdgeProcessor.java | 2 +- .../processor/device/DeviceEdgeProcessor.java | 2 +- .../telemetry/BaseTelemetryProcessor.java | 6 +- .../entitiy/EntityStateSourcingListener.java | 2 +- .../rpc/DefaultTbCoreDeviceRpcService.java | 2 +- .../server/service/rpc/TbRpcService.java | 2 +- .../state/DefaultDeviceStateService.java | 2 +- .../transport/DefaultTransportApiService.java | 2 +- .../actors/rule/DefaultTbContextTest.java | 6 +- .../controller/RuleEngineControllerTest.java | 10 +-- .../controller/TenantControllerTest.java | 2 +- ...AbstractRuleEngineFlowIntegrationTest.java | 4 +- ...actRuleEngineLifecycleIntegrationTest.java | 2 +- .../queue/DefaultTbClusterServiceTest.java | 16 ++-- .../TbRuleEngineQueueConsumerManagerTest.java | 2 +- .../ruleengine/TbRuleEngineStrategyTest.java | 2 +- .../DefaultTbRuleEngineRpcServiceTest.java | 2 +- .../DefaultRuleEngineCallServiceTest.java | 4 +- .../SequentialTimeseriesPersistenceTest.java | 2 +- .../thingsboard/server/common/msg/TbMsg.java | 34 +++++-- .../common/TbRuleEngineProducerService.java | 2 +- .../service/DefaultTransportService.java | 2 +- .../rule/engine/api/util/TbNodeUtilsTest.java | 10 +-- .../rule/engine/action/TbMsgCountNode.java | 2 +- .../deduplication/TbMsgDeduplicationNode.java | 4 +- .../rule/engine/delay/TbMsgDelayNode.java | 2 +- .../engine/profile/TbDeviceProfileNode.java | 6 +- .../action/TbAssignToCustomerNodeTest.java | 2 +- .../engine/action/TbClearAlarmNodeTest.java | 4 +- .../TbCopyAttributesToEntityViewNodeTest.java | 12 +-- .../engine/action/TbCreateAlarmNodeTest.java | 14 +-- .../action/TbCreateRelationNodeTest.java | 4 +- .../action/TbDeleteRelationNodeTest.java | 2 +- .../engine/action/TbDeviceStateNodeTest.java | 6 +- .../rule/engine/action/TbLogNodeTest.java | 6 +- .../engine/action/TbMsgCountNodeTest.java | 6 +- .../TbSaveToCustomCassandraTableNodeTest.java | 10 +-- .../TbUnassignFromCustomerNodeTest.java | 2 +- .../aws/lambda/TbAwsLambdaNodeTest.java | 10 +-- .../rule/engine/aws/sns/TbSnsNodeTest.java | 4 +- .../rule/engine/aws/sqs/TbSqsNodeTest.java | 8 +- .../engine/debug/TbMsgGeneratorNodeTest.java | 8 +- .../engine/edge/TbMsgPushToEdgeNodeTest.java | 6 +- .../filter/TbAssetTypeSwitchNodeTest.java | 2 +- .../filter/TbCheckAlarmStatusNodeTest.java | 2 +- .../engine/filter/TbCheckMessageNodeTest.java | 4 +- .../filter/TbCheckRelationNodeTest.java | 2 +- .../filter/TbDeviceTypeSwitchNodeTest.java | 2 +- .../engine/filter/TbJsFilterNodeTest.java | 6 +- .../engine/filter/TbJsSwitchNodeTest.java | 2 +- .../filter/TbMsgTypeFilterNodeTest.java | 2 +- .../filter/TbMsgTypeSwitchNodeTest.java | 2 +- .../TbOriginatorTypeFilterNodeTest.java | 2 +- .../TbOriginatorTypeSwitchNodeTest.java | 2 +- .../rule/engine/flow/TbAckNodeTest.java | 2 +- .../engine/flow/TbCheckpointNodeTest.java | 4 +- .../engine/flow/TbRuleChainInputNodeTest.java | 2 +- .../flow/TbRuleChainOutputNodeTest.java | 2 +- .../engine/gcp/pubsub/TbPubSubNodeTest.java | 8 +- .../geo/TbGpsGeofencingActionNodeTest.java | 2 +- .../geo/TbGpsGeofencingFilterNodeTest.java | 4 +- .../rule/engine/kafka/TbKafkaNodeTest.java | 12 +-- .../engine/mail/TbMsgToEmailNodeTest.java | 2 +- .../rule/engine/math/TbMathNodeTest.java | 38 ++++---- .../metadata/CalculateDeltaNodeTest.java | 32 +++---- .../TbFetchDeviceCredentialsNodeTest.java | 2 +- .../metadata/TbGetAttributesNodeTest.java | 4 +- .../TbGetCustomerAttributeNodeTest.java | 6 +- .../TbGetCustomerDetailsNodeTest.java | 4 +- .../metadata/TbGetDeviceAttrNodeTest.java | 2 +- .../TbGetOriginatorFieldsNodeTest.java | 12 +-- .../TbGetRelatedAttributeNodeTest.java | 4 +- .../metadata/TbGetTelemetryNodeTest.java | 20 ++--- .../TbGetTenantAttributeNodeTest.java | 4 +- .../metadata/TbGetTenantDetailsNodeTest.java | 4 +- .../rule/engine/mqtt/TbMqttNodeTest.java | 4 +- .../rule/engine/profile/DeviceStateTest.java | 14 +-- .../profile/TbDeviceProfileNodeTest.java | 90 +++++++++---------- .../engine/rabbitmq/TbRabbitMqNodeTest.java | 6 +- .../rule/engine/rest/TbHttpClientTest.java | 4 +- .../engine/rest/TbRestApiCallNodeTest.java | 4 +- .../rest/TbSendRestApiCallReplyNodeTest.java | 4 +- .../engine/rpc/TbSendRPCReplyNodeTest.java | 8 +- .../engine/rpc/TbSendRPCRequestNodeTest.java | 40 ++++----- .../telemetry/TbMsgAttributesNodeTest.java | 2 +- .../TbMsgDeleteAttributesNodeTest.java | 2 +- .../telemetry/TbMsgTimeseriesNodeTest.java | 8 +- .../transform/TbChangeOriginatorNodeTest.java | 12 +-- .../engine/transform/TbCopyKeysNodeTest.java | 2 +- .../transform/TbDeleteKeysNodeTest.java | 2 +- .../engine/transform/TbJsonPathNodeTest.java | 2 +- .../transform/TbMsgDeduplicationNodeTest.java | 4 +- .../transform/TbRenameKeysNodeTest.java | 2 +- .../transform/TbSplitArrayMsgNodeTest.java | 2 +- .../transform/TbTransformMsgNodeTest.java | 6 +- 102 files changed, 349 insertions(+), 327 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index e456f9dd46..0c3c2b749d 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -363,7 +363,7 @@ public class DefaultTbContext implements TbContext { @Override public TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { - return TbMsg.builder() + return TbMsg.newMsg() .queueName(queueName) .type(type) .originator(originator) @@ -387,7 +387,7 @@ public class DefaultTbContext implements TbContext { @Override public TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { - return TbMsg.builder() + return TbMsg.newMsg() .queueName(queueName) .type(type) .originator(originator) @@ -510,7 +510,7 @@ public class DefaultTbContext implements TbContext { defaultQueueName = profile.getDefaultQueueName(); defaultRuleChainId = profile.getDefaultRuleChainId(); } - return TbMsg.builder() + return TbMsg.newMsg() .queueName(defaultQueueName) .type(action) .originator(id) @@ -536,7 +536,7 @@ public class DefaultTbContext implements TbContext { defaultQueueName = profile.getDefaultQueueName(); defaultRuleChainId = profile.getDefaultRuleChainId(); } - return TbMsg.builder() + return TbMsg.newMsg() .queueName(defaultQueueName) .type(actionMsgType) .originator(id) diff --git a/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java b/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java index 51a13321e7..98269d38ac 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java @@ -239,7 +239,7 @@ public class RpcV2Controller extends AbstractRpcController { rpcService.deleteRpc(getTenantId(), rpcId); rpc.setStatus(RpcStatus.DELETED); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_DELETED) .originator(rpc.getDeviceId()) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java index ba7e8e87bc..73e7966666 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -384,7 +384,7 @@ public class RuleChainController extends BaseController { } engine = new RuleNodeTbelScriptEngine(getTenantId(), tbelInvokeService, script, argNames); } - TbMsg inMsg = TbMsg.builder() + TbMsg inMsg = TbMsg.newMsg() .type(msgType) .originator(null) .metaData(new TbMsgMetaData(metadata).copy()) diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleEngineController.java b/application/src/main/java/org/thingsboard/server/controller/RuleEngineController.java index 6484553f80..6fe0d5fd52 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleEngineController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleEngineController.java @@ -169,7 +169,7 @@ public class RuleEngineController extends BaseController { metaData.put("serviceId", serviceInfoProvider.getServiceId()); metaData.put("requestUUID", requestId.toString()); metaData.put("expirationTime", Long.toString(expTime)); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .queueName(queueName) .type(TbMsgType.REST_API_REQUEST) .originator(entityId) diff --git a/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java b/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java index e743fc7a4e..8089b32f3c 100644 --- a/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java +++ b/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java @@ -172,7 +172,7 @@ public class EntityActionService { if (tenantId != null && !tenantId.isSysTenantId()) { processNotificationRules(tenantId, entityId, entity, actionType, user, additionalInfo); } - TbMsg tbMsg = TbMsg.builder() + TbMsg tbMsg = TbMsg.newMsg() .type(msgType.get()) .originator(entityId) .customerId(customerId) diff --git a/application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java b/application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java index 59f835b0a1..4265747ee8 100644 --- a/application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java +++ b/application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java @@ -253,7 +253,7 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { private void pushProvisionEventToRuleEngine(ProvisionRequest request, Device device, TbMsgType type) { try { JsonNode entityNode = JacksonUtil.valueToTree(request); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(type) .originator(device.getId()) .customerId(device.getCustomerId()) @@ -269,7 +269,7 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { private void pushDeviceCreatedEventToRuleEngine(Device device) { try { ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(device); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.ENTITY_CREATED) .originator(device.getId()) .customerId(device.getCustomerId()) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java index bef7c90632..ae377ac657 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java @@ -575,7 +575,7 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i md.putValue("edgeName", edge.getName()); md.putValue("edgeType", edge.getType()); } - TbMsg tbMsg = TbMsg.builder() + TbMsg tbMsg = TbMsg.newMsg() .type(msgType) .originator(edgeId) .metaData(md.copy()) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java index f7981f81db..c7526b574e 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java @@ -343,7 +343,7 @@ public abstract class BaseEdgeProcessor { protected void pushEntityEventToRuleEngine(TenantId tenantId, EntityId entityId, CustomerId customerId, TbMsgType msgType, String msgData, TbMsgMetaData metaData) { - TbMsg tbMsg = TbMsg.builder() + TbMsg tbMsg = TbMsg.newMsg() .type(msgType) .originator(entityId) .customerId(customerId) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java index eae19082c5..a68cb101a0 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java @@ -190,7 +190,7 @@ public abstract class DeviceEdgeProcessor extends BaseDeviceProcessor implements ObjectNode data = JacksonUtil.newObjectNode(); data.put("method", deviceRpcCallMsg.getRequestMsg().getMethod()); data.put("params", deviceRpcCallMsg.getRequestMsg().getParams()); - TbMsg tbMsg = TbMsg.builder() + TbMsg tbMsg = TbMsg.newMsg() .type(TbMsgType.TO_SERVER_RPC_REQUEST) .originator(deviceId) .metaData(metaData.copy()) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java index bc425bd624..a524ccca21 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java @@ -208,7 +208,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList()); metaData.putValue("ts", tsKv.getTs() + ""); var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); - TbMsg tbMsg = TbMsg.builder() + TbMsg tbMsg = TbMsg.newMsg() .queueName(defaultQueueAndRuleChain.getKey()) .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(entityId) @@ -261,7 +261,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { SettableFuture futureToSet = SettableFuture.create(); JsonObject json = JsonUtils.getJsonObject(msg.getKvList()); var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); - TbMsg tbMsg = TbMsg.builder() + TbMsg tbMsg = TbMsg.newMsg() .queueName(defaultQueueAndRuleChain.getKey()) .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) @@ -299,7 +299,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { @Override public void onSuccess(@Nullable Void tmp) { var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); - TbMsg tbMsg = TbMsg.builder() + TbMsg tbMsg = TbMsg.newMsg() .queueName(defaultQueueAndRuleChain.getKey()) .type(TbMsgType.ATTRIBUTES_UPDATED) .originator(entityId) diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java b/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java index 2fa2bfd0e2..288afcb081 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java @@ -250,7 +250,7 @@ public class EntityStateSourcingListener { private void pushAssignedFromNotification(Tenant currentTenant, TenantId newTenantId, Device assignedDevice) { String data = JacksonUtil.toString(JacksonUtil.valueToTree(assignedDevice)); if (data != null) { - TbMsg tbMsg = TbMsg.builder() + TbMsg tbMsg = TbMsg.newMsg() .type(TbMsgType.ENTITY_ASSIGNED_FROM_TENANT) .originator(assignedDevice.getId()) .customerId(assignedDevice.getCustomerId()) diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java index 0e65e55243..49c0b6cc30 100644 --- a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java @@ -183,7 +183,7 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService { entityNode.put(DataConstants.ADDITIONAL_INFO, msg.getAdditionalInfo()); try { - TbMsg tbMsg = TbMsg.builder() + TbMsg tbMsg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(msg.getDeviceId()) .customerId(Optional.ofNullable(currentUser).map(User::getCustomerId).orElse(null)) diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java b/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java index abe4679afd..5936238e39 100644 --- a/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java @@ -63,7 +63,7 @@ public class TbRpcService { } private void pushRpcMsgToRuleEngine(TenantId tenantId, Rpc rpc) { - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.valueOf("RPC_" + rpc.getStatus().name())) .originator(rpc.getDeviceId()) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java index 18315c0372..6285778efc 100644 --- a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java @@ -857,7 +857,7 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService devices, Asset asset, int idx) throws ExecutionException, InterruptedException, TimeoutException { for (Device device : devices) { - TbMsg tbMsg = TbMsg.builder() + TbMsg tbMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(device.getId()) .metaData(getTbMsgMetadata(device.getName(), ts.get(idx)).copy()) diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java index b987a73a6e..66c48077c2 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java @@ -99,8 +99,13 @@ public final class TbMsg implements Serializable { */ @Deprecated(since = "3.6.0") public static TbMsg transformMsg(TbMsg tbMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, null, type, originator, tbMsg.customerId, metaData.copy(), tbMsg.dataType, - data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.callback); + return tbMsg.transform() + .type(type) + .originator(originator) + .metaData(metaData) + .data(data) + .ctx(tbMsg.ctx) + .build(); } public static TbMsg transformMsg(TbMsg tbMsg, TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data) { @@ -347,12 +352,12 @@ public final class TbMsg implements Serializable { return false; } - public static TbMsgBuilder builder() { + public static TbMsgBuilder newMsg() { return new TbMsgBuilder(); } - public TbMsgBuilder toBuilder() { - return new TbMsgBuilder() + public TbMsgBuilder transform() { + return new TbMsgTransformer() .queueName(this.queueName) .id(this.id) .ts(this.ts) @@ -371,7 +376,23 @@ public final class TbMsg implements Serializable { .callback(this.callback); } - public static class TbMsgBuilder { + private static class TbMsgTransformer extends TbMsgBuilder { + + @Override + public TbMsgTransformer metaData(TbMsgMetaData metaData) { + super.metaData(metaData.copy()); + return this; + } + + @Override + public TbMsgTransformer ctx(TbMsgProcessingCtx ctx) { + super.ctx(ctx.copy()); + return this; + } + + } + + private static class TbMsgBuilder { private String queueName; private UUID id; @@ -415,6 +436,7 @@ public final class TbMsg implements Serializable { @Deprecated public TbMsgBuilder type(String type) { this.type = type; + this.internalType = null; return this; } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/TbRuleEngineProducerService.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/TbRuleEngineProducerService.java index 49b40e3a6d..09f1f4608d 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/TbRuleEngineProducerService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/TbRuleEngineProducerService.java @@ -47,7 +47,7 @@ public class TbRuleEngineProducerService { Integer partition = tpi.getPartition().orElse(null); UUID id = i > 0 ? UUID.randomUUID() : tbMsg.getId(); - tbMsg = tbMsg.toBuilder() + tbMsg = tbMsg.transform() .id(id) .correlationId(correlationId) .partition(partition) diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java index e15d27a1e0..85e8b4cfd1 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java @@ -1136,7 +1136,7 @@ public class DefaultTransportService extends TransportActivityManager implements queueName = deviceProfile.getDefaultQueueName(); } - TbMsg tbMsg = TbMsg.builder() + TbMsg tbMsg = TbMsg.newMsg() .queueName(queueName) .type(tbMsgType) .originator(deviceId) diff --git a/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/util/TbNodeUtilsTest.java b/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/util/TbNodeUtilsTest.java index 895b527d6a..38db78d221 100644 --- a/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/util/TbNodeUtilsTest.java +++ b/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/util/TbNodeUtilsTest.java @@ -44,7 +44,7 @@ public class TbNodeUtilsTest { ObjectNode node = JacksonUtil.newObjectNode(); node.put("data_key", "data_value"); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(TenantId.SYS_TENANT_ID) .metaData(md.copy()) @@ -63,7 +63,7 @@ public class TbNodeUtilsTest { ObjectNode node = JacksonUtil.newObjectNode(); node.put("key", "data_value"); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(TenantId.SYS_TENANT_ID) .metaData(md.copy()) @@ -82,7 +82,7 @@ public class TbNodeUtilsTest { ObjectNode node = JacksonUtil.newObjectNode(); node.put("key", "data_value"); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(TenantId.SYS_TENANT_ID) .metaData(md.copy()) @@ -108,7 +108,7 @@ public class TbNodeUtilsTest { ObjectNode node = JacksonUtil.newObjectNode(); node.set("key1", key1Node); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(TenantId.SYS_TENANT_ID) .metaData(md.copy()) @@ -134,7 +134,7 @@ public class TbNodeUtilsTest { ObjectNode node = JacksonUtil.newObjectNode(); node.set("key1", key1Node); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(TenantId.SYS_TENANT_ID) .metaData(md.copy()) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java index c6619b508c..f54f442e57 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java @@ -74,7 +74,7 @@ public class TbMsgCountNode implements TbNode { TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("delta", Long.toString(System.currentTimeMillis() - lastScheduledTs + delay)); - TbMsg tbMsg = TbMsg.builder() + TbMsg tbMsg = TbMsg.newMsg() .queueName(msg.getQueueName()) .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(ctx.getTenantId()) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java index e16528d5d3..4644696886 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java @@ -154,7 +154,7 @@ public class TbMsgDeduplicationNode implements TbNode { iterator.remove(); } } - deduplicationResults.add(TbMsg.builder() + deduplicationResults.add(TbMsg.newMsg() .queueName(queueName) .type(config.getOutMsgType()) .originator(deduplicationId) @@ -178,7 +178,7 @@ public class TbMsgDeduplicationNode implements TbNode { } if (resultMsg != null) { String queueName1 = queueName != null ? queueName : resultMsg.getQueueName(); - deduplicationResults.add(TbMsg.builder() + deduplicationResults.add(TbMsg.newMsg() .queueName(queueName1) .type(resultMsg.getType()) .originator(resultMsg.getOriginator()) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java index 8331f066d8..e3887db026 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java @@ -65,7 +65,7 @@ public class TbMsgDelayNode implements TbNode { TbMsg pendingMsg = pendingMsgs.remove(UUID.fromString(msg.getData())); if (pendingMsg != null) { ctx.enqueueForTellNext( - TbMsg.builder() + TbMsg.newMsg() .queueName(pendingMsg.getQueueName()) .type(pendingMsg.getType()) .originator(pendingMsg.getOriginator()) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java index a1ff3dc2d4..e1f0531195 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java @@ -178,7 +178,7 @@ public class TbDeviceProfileNode implements TbNode { protected void scheduleAlarmHarvesting(TbContext ctx, TbMsg msg) { CustomerId customerId = msg != null ? msg.getCustomerId() : null; - TbMsg periodicCheck = TbMsg.builder() + TbMsg periodicCheck = TbMsg.newMsg() .type(TbMsgType.DEVICE_PROFILE_PERIODIC_SELF_MSG) .originator(ctx.getTenantId()) .customerId(customerId) @@ -209,7 +209,7 @@ public class TbDeviceProfileNode implements TbNode { } protected void onProfileUpdate(DeviceProfile profile) { - ctx.tellSelf(TbMsg.builder() + ctx.tellSelf(TbMsg.newMsg() .type(TbMsgType.DEVICE_PROFILE_UPDATE_SELF_MSG) .originator(ctx.getTenantId()) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -223,7 +223,7 @@ public class TbDeviceProfileNode implements TbNode { if (deviceProfile != null) { msgData.put("deviceProfileId", deviceProfile.getId().getId().toString()); } - ctx.tellSelf(TbMsg.builder() + ctx.tellSelf(TbMsg.newMsg() .type(TbMsgType.DEVICE_UPDATE_SELF_MSG) .originator(ctx.getTenantId()) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNodeTest.java index 728b1a88d7..b6cdb0b7d3 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNodeTest.java @@ -306,7 +306,7 @@ class TbAssignToCustomerNodeTest extends AbstractRuleNodeUpgradeTest { } private TbMsg getTbMsg(EntityId originator) { - return TbMsg.builder() + return TbMsg.newMsg() .type(TbMsgType.NA) .originator(originator) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbClearAlarmNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbClearAlarmNodeTest.java index 177c2349fb..e8deedba09 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbClearAlarmNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbClearAlarmNodeTest.java @@ -91,7 +91,7 @@ class TbClearAlarmNodeTest { void alarmCanBeCleared() { initWithClearAlarmScript(); metadata.putValue("key", "value"); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(msgOriginator) .metaData(metadata.copy()) @@ -148,7 +148,7 @@ class TbClearAlarmNodeTest { void alarmCanBeClearedWithAlarmOriginator() { initWithClearAlarmScript(); metadata.putValue("key", "value"); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(alarmOriginator) .metaData(metadata.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNodeTest.java index b676816d4f..b0d900c7e2 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNodeTest.java @@ -106,7 +106,7 @@ public class TbCopyAttributesToEntityViewNodeTest { public void givenExistingClientAttributes_whenOnMsg_thenCopyAttributesToView() { EntityView entityView = getEntityView(CLIENT_TELEMETRY_ENTITY_VIEW); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(DEVICE_ID) .metaData(new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name())).copy()) @@ -143,7 +143,7 @@ public class TbCopyAttributesToEntityViewNodeTest { public void givenExistingServerAttributesAndMsgTypeAttributesDeleted_whenOnMsg_thenDeleteAttributesFromView() { EntityView entityView = getEntityView(SERVER_TELEMETRY_ENTITY_VIEW); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(ATTRIBUTES_DELETED) .originator(DEVICE_ID) .metaData(new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name())).copy()) @@ -177,7 +177,7 @@ public class TbCopyAttributesToEntityViewNodeTest { public void givenNonMatchedSharedAttributesAndMsgTypeIsAttributesDeleted_whenOnMsg_thenNoAttributesDeleteFromView() { EntityView entityView = getEntityView(SHARED_TELEMETRY_ENTITY_VIEW); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.ATTRIBUTES_DELETED) .originator(DEVICE_ID) .metaData(new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SHARED_SCOPE.name())).copy()) @@ -197,7 +197,7 @@ public class TbCopyAttributesToEntityViewNodeTest { public void givenNonMatchedAttributesAndMsgTypeIsPostAttributesRequest_whenOnMsg_thenCopyNoAttributesToView() { EntityView entityView = getEntityView(CLIENT_TELEMETRY_ENTITY_VIEW); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(DEVICE_ID) .metaData(new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name())).copy()) @@ -232,7 +232,7 @@ public class TbCopyAttributesToEntityViewNodeTest { ); mockEntityViewLookup(entityView); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(ATTRIBUTES_DELETED) .originator(DEVICE_ID) .metaData(new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name())).copy()) @@ -248,7 +248,7 @@ public class TbCopyAttributesToEntityViewNodeTest { @ParameterizedTest @EnumSource(TbMsgType.class) public void givenMsgTypeAndEmptyMetadata_whenOnMsg_thenVerifyFailureMsg(TbMsgType msgType) { - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(msgType) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java index a73aa72cec..7846ac2da5 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java @@ -163,7 +163,7 @@ class TbCreateAlarmNodeTest { var ruleNodeSelfId = new RuleNodeId(Uuids.timeBased()); - var incomingMsg = TbMsg.builder() + var incomingMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(msgOriginator) .metaData(metadata.copy()) @@ -322,7 +322,7 @@ class TbCreateAlarmNodeTest { var ruleNodeSelfId = new RuleNodeId(Uuids.timeBased()); - var incomingMsg = TbMsg.builder() + var incomingMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(msgOriginator) .metaData(metadata.copy()) @@ -518,7 +518,7 @@ class TbCreateAlarmNodeTest { var ruleNodeSelfId = new RuleNodeId(Uuids.timeBased()); - var incomingMsg = TbMsg.builder() + var incomingMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(msgOriginator) .metaData(metadata.copy()) @@ -695,7 +695,7 @@ class TbCreateAlarmNodeTest { var ruleNodeSelfId = new RuleNodeId(Uuids.timeBased()); - var incomingMsg = TbMsg.builder() + var incomingMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(msgOriginator) .metaData(metadata.copy()) @@ -887,7 +887,7 @@ class TbCreateAlarmNodeTest { .details(newAlarmDetails) .build(); - var incomingMsg = TbMsg.builder() + var incomingMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(msgOriginator) .metaData(metadata.copy()) @@ -1073,7 +1073,7 @@ class TbCreateAlarmNodeTest { .details(alarmDetails) .build(); - var incomingMsg = TbMsg.builder() + var incomingMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(msgOriginator) .metaData(metadata.copy()) @@ -1219,7 +1219,7 @@ class TbCreateAlarmNodeTest { // GIVEN config = config.defaultConfiguration(); - var incomingMsg = TbMsg.builder() + var incomingMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(msgOriginator) .metaData(metadata.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateRelationNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateRelationNodeTest.java index d287bb8e01..6e707a4415 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateRelationNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateRelationNodeTest.java @@ -486,7 +486,7 @@ public class TbCreateRelationNodeTest extends AbstractRuleNodeUpgradeTest { when(ctxMock.getRelationService()).thenReturn(relationServiceMock); var mockMethodCallsMap = mockEntityServiceCallsCreateEntityIfNotExistsEnabled(); - var entityCreatedMsg = TbMsg.builder() + var entityCreatedMsg = TbMsg.newMsg() .type(TbMsgType.ENTITY_CREATED) .originator(entityId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -681,7 +681,7 @@ public class TbCreateRelationNodeTest extends AbstractRuleNodeUpgradeTest { } private TbMsg getTbMsg(EntityId originator, TbMsgMetaData metaData) { - return TbMsg.builder() + return TbMsg.newMsg() .type(TbMsgType.NA) .originator(originator) .metaData(metaData.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbDeleteRelationNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbDeleteRelationNodeTest.java index 8e9295b3e0..9a2eeb114e 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbDeleteRelationNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbDeleteRelationNodeTest.java @@ -560,7 +560,7 @@ public class TbDeleteRelationNodeTest extends AbstractRuleNodeUpgradeTest { } private TbMsg getTbMsg(EntityId originator, TbMsgMetaData metaData) { - return TbMsg.builder() + return TbMsg.newMsg() .type(TbMsgType.NA) .originator(originator) .metaData(metaData.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbDeviceStateNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbDeviceStateNodeTest.java index 1f8dcd61be..12e16ab8c6 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbDeviceStateNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbDeviceStateNodeTest.java @@ -85,7 +85,7 @@ public class TbDeviceStateNodeTest { metaData.putValue("ts", String.valueOf(METADATA_TS)); var data = JacksonUtil.newObjectNode(); data.put("humidity", 58.3); - msg = TbMsg.builder() + msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(metaData.copy()) @@ -212,7 +212,7 @@ public class TbDeviceStateNodeTest { return unsupportedType; } }; - var msg = TbMsg.builder() + var msg = TbMsg.newMsg() .type(TbMsgType.ENTITY_CREATED) .originator(nonDeviceOriginator) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -246,7 +246,7 @@ public class TbDeviceStateNodeTest { given(ctxMock.getDeviceStateManager()).willReturn(deviceStateManagerMock); long msgTs = METADATA_TS + 1; - msg = TbMsg.builder() + msg = TbMsg.newMsg() .ts(msgTs) .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbLogNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbLogNodeTest.java index 63e5639d43..cd40154e5e 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbLogNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbLogNodeTest.java @@ -50,7 +50,7 @@ public class TbLogNodeTest { TbLogNode node = new TbLogNode(); String data = "{\"key\": \"value\"}"; TbMsgMetaData metaData = new TbMsgMetaData(Map.of("mdKey1", "mdValue1", "mdKey2", "23")); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(TenantId.SYS_TENANT_ID) .metaData(metaData.copy()) @@ -71,7 +71,7 @@ public class TbLogNodeTest { void givenEmptyDataMsg_whenToLog_thenReturnString() { TbLogNode node = new TbLogNode(); TbMsgMetaData metaData = new TbMsgMetaData(Collections.emptyMap()); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(TenantId.SYS_TENANT_ID) .metaData(metaData.copy()) @@ -92,7 +92,7 @@ public class TbLogNodeTest { void givenNullDataMsg_whenToLog_thenReturnString() { TbLogNode node = new TbLogNode(); TbMsgMetaData metaData = new TbMsgMetaData(Collections.emptyMap()); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(TenantId.SYS_TENANT_ID) .metaData(metaData.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbMsgCountNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbMsgCountNodeTest.java index 3c71b09b7d..a29801a92d 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbMsgCountNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbMsgCountNodeTest.java @@ -63,7 +63,7 @@ public class TbMsgCountNodeTest { private final DeviceId DEVICE_ID = new DeviceId(UUID.fromString("1b21c7cc-0c9e-4ab1-b867-99451599e146")); private final TenantId TENANT_ID = TenantId.fromUUID(UUID.fromString("04dfbd38-10e5-47b7-925f-11e795db89e1")); - private final TbMsg tickMsg = TbMsg.builder() + private final TbMsg tickMsg = TbMsg.newMsg() .type(TbMsgType.MSG_COUNT_SELF_MSG) .originator(RULE_NODE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -125,7 +125,7 @@ public class TbMsgCountNodeTest { var expectedProcessedMsgs = new ArrayList(); for (int i = 0; i < msgCount; i++) { - var msg = TbMsg.builder() + var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -152,7 +152,7 @@ public class TbMsgCountNodeTest { then(ctxMock).should().enqueueForTellNext(msgWithCounterCaptor.capture(), eq(TbNodeConnectionType.SUCCESS)); TbMsg resultedMsg = msgWithCounterCaptor.getValue(); String expectedData = "{\"messageCount_tb-rule-engine\":" + currentMsgNumber + "}"; - TbMsg expectedMsg = TbMsg.builder() + TbMsg expectedMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(TENANT_ID) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNodeTest.java index e211ee6514..98e32474a3 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNodeTest.java @@ -185,7 +185,7 @@ public class TbSaveToCustomCassandraTableNodeTest extends AbstractRuleNodeUpgrad node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -211,7 +211,7 @@ public class TbSaveToCustomCassandraTableNodeTest extends AbstractRuleNodeUpgrad "humidity": 77 } """; - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -237,7 +237,7 @@ public class TbSaveToCustomCassandraTableNodeTest extends AbstractRuleNodeUpgrad "temp": [value] } """; - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -264,7 +264,7 @@ public class TbSaveToCustomCassandraTableNodeTest extends AbstractRuleNodeUpgrad mockSubmittingCassandraTask(); node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -319,7 +319,7 @@ public class TbSaveToCustomCassandraTableNodeTest extends AbstractRuleNodeUpgrad } } """; - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNodeTest.java index 65be0c5cff..971a858758 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNodeTest.java @@ -294,7 +294,7 @@ class TbUnassignFromCustomerNodeTest extends AbstractRuleNodeUpgradeTest { } private TbMsg getTbMsg(EntityId originator) { - return TbMsg.builder() + return TbMsg.newMsg() .type(TbMsgType.NA) .originator(originator) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNodeTest.java index 7a96720292..e0e3d6d0d4 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNodeTest.java @@ -145,7 +145,7 @@ public class TbAwsLambdaNodeTest { config.setFunctionName(functionName); config.setQualifier(qualifier); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(metadata.copy()) @@ -202,7 +202,7 @@ public class TbAwsLambdaNodeTest { init(); config.setTellFailureIfFuncThrowsExc(true); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -243,7 +243,7 @@ public class TbAwsLambdaNodeTest { public void givenExceptionWasThrownInsideFunctionAndTellFailureIfFuncThrowsExcIsFalse_whenOnMsg_thenTellSuccess() { init(); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -281,7 +281,7 @@ public class TbAwsLambdaNodeTest { public void givenPayloadFromResultIsNull_whenOnMsg_thenTellFailure() { init(); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -320,7 +320,7 @@ public class TbAwsLambdaNodeTest { @Test public void givenExceptionWasThrownOnAWS_whenOnMsg_thenTellFailure() { init(); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sns/TbSnsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sns/TbSnsNodeTest.java index d6161a14ae..8fc649d10c 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sns/TbSnsNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sns/TbSnsNodeTest.java @@ -105,7 +105,7 @@ class TbSnsNodeTest { given(publishResultMock.getSdkResponseMetadata()).willReturn(responseMetadataMock); given(responseMetadataMock.getRequestId()).willReturn(requestId); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(metaData.copy()) @@ -148,7 +148,7 @@ class TbSnsNodeTest { ListenableFuture failedFuture = Futures.immediateFailedFuture(new RuntimeException(errorMsg)); given(listeningExecutor.executeAsync(any(Callable.class))).willReturn(failedFuture); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNodeTest.java index f32ddf665c..60785d409e 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNodeTest.java @@ -107,7 +107,7 @@ class TbSqsNodeTest { mockSendingMsgRequest(); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(metaData.copy()) @@ -148,7 +148,7 @@ class TbSqsNodeTest { mockSendingMsgRequest(); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(metaData.copy()) @@ -196,7 +196,7 @@ class TbSqsNodeTest { given(sendMessageResultMock.getMD5OfMessageAttributes()).willReturn(messageAttributesMd5); given(sendMessageResultMock.getSequenceNumber()).willReturn(sequenceNumber); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -236,7 +236,7 @@ class TbSqsNodeTest { ListenableFuture failedFuture = Futures.immediateFailedFuture(new RuntimeException(errorMsg)); given(listeningExecutor.executeAsync(any(Callable.class))).willReturn(failedFuture); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNodeTest.java index 9d1ede5bd6..5d40328ae4 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNodeTest.java @@ -190,7 +190,7 @@ public class TbMsgGeneratorNodeTest extends AbstractRuleNodeUpgradeTest { given(ctxMock.createScriptEngine(any(), any(), any(), any(), any())).willReturn(scriptEngineMock); // creation of tickMsg - TbMsg tickMsg = TbMsg.builder() + TbMsg tickMsg = TbMsg.newMsg() .type(TbMsgType.GENERATOR_NODE_SELF_MSG) .originator(RULE_NODE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -208,7 +208,7 @@ public class TbMsgGeneratorNodeTest extends AbstractRuleNodeUpgradeTest { }).given(ctxMock).tellSelf(any(), any(Long.class)); // creation of first message - TbMsg firstMsg = TbMsg.builder() + TbMsg firstMsg = TbMsg.newMsg() .type(TbMsg.EMPTY_STRING) .originator(RULE_NODE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -218,7 +218,7 @@ public class TbMsgGeneratorNodeTest extends AbstractRuleNodeUpgradeTest { // creation of generated message TbMsgMetaData metaData = new TbMsgMetaData(Map.of("data", "40")); - TbMsg generatedMsg = TbMsg.builder() + TbMsg generatedMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(RULE_NODE_ID) .metaData(metaData.copy()) @@ -227,7 +227,7 @@ public class TbMsgGeneratorNodeTest extends AbstractRuleNodeUpgradeTest { given(scriptEngineMock.executeGenerateAsync(any())).willReturn(Futures.immediateFuture(generatedMsg)); // creation of prev message - TbMsg prevMsg = TbMsg.builder() + TbMsg prevMsg = TbMsg.newMsg() .type(generatedMsg.getType()) .originator(RULE_NODE_ID) .metaData(generatedMsg.getMetaData().copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNodeTest.java index e45e5fc0f1..e84850498d 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNodeTest.java @@ -85,7 +85,7 @@ public class TbMsgPushToEdgeNodeTest { Mockito.when(ctx.getEdgeService()).thenReturn(edgeService); Mockito.when(edgeService.findRelatedEdgeIdsByEntityId(tenantId, deviceId, new PageLink(RELATED_EDGES_CACHE_ITEMS))).thenReturn(new PageData<>()); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -113,7 +113,7 @@ public class TbMsgPushToEdgeNodeTest { PageData edgePageData = new PageData<>(List.of(edgeId), 1, 1, false); Mockito.when(edgeService.findRelatedEdgeIdsByEntityId(tenantId, userId, new PageLink(RELATED_EDGES_CACHE_ITEMS))).thenReturn(edgePageData); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.ATTRIBUTES_UPDATED) .originator(userId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -151,7 +151,7 @@ public class TbMsgPushToEdgeNodeTest { Mockito.when(ctx.getDbCallbackExecutor()).thenReturn(dbCallbackExecutor); Mockito.when(edgeEventService.saveAsync(any())).thenReturn(SettableFuture.create()); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(event) .originator(new EdgeId(UUID.randomUUID())) .metaData(metaData.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNodeTest.java index 22c7997983..eb99770537 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNodeTest.java @@ -118,7 +118,7 @@ class TbAssetTypeSwitchNodeTest { } private TbMsg getTbMsg(EntityId entityId) { - return TbMsg.builder() + return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java index 5aa9b9769f..012303278f 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java @@ -174,7 +174,7 @@ class TbCheckAlarmStatusNodeTest { } private TbMsg getTbMsg(String msgData) { - return TbMsg.builder() + return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckMessageNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckMessageNodeTest.java index a1dcd7d526..377c0b69e7 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckMessageNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckMessageNodeTest.java @@ -44,7 +44,7 @@ import static org.mockito.Mockito.verify; class TbCheckMessageNodeTest { private static final DeviceId DEVICE_ID = new DeviceId(UUID.randomUUID()); - private static final TbMsg EMPTY_POST_ATTRIBUTES_MSG = TbMsg.builder() + private static final TbMsg EMPTY_POST_ATTRIBUTES_MSG = TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -201,7 +201,7 @@ class TbCheckMessageNodeTest { metadata.putValue(DataConstants.DEVICE_NAME, "Test Device"); metadata.putValue(DataConstants.DEVICE_TYPE, DataConstants.DEFAULT_DEVICE_TYPE); metadata.putValue("ts", String.valueOf(System.currentTimeMillis())); - return TbMsg.builder() + return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(DEVICE_ID) .metaData(metadata.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeTest.java index d6a9f98ac0..9ddf70b9dc 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeTest.java @@ -66,7 +66,7 @@ class TbCheckRelationNodeTest extends AbstractRuleNodeUpgradeTest { private final TenantId TENANT_ID = new TenantId(UUID.randomUUID()); private final DeviceId ORIGINATOR_ID = new DeviceId(UUID.randomUUID()); private final TestDbCallbackExecutor DB_EXECUTOR = new TestDbCallbackExecutor(); - private final TbMsg EMPTY_POST_ATTRIBUTES_MSG = TbMsg.builder() + private final TbMsg EMPTY_POST_ATTRIBUTES_MSG = TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(ORIGINATOR_ID) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbDeviceTypeSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbDeviceTypeSwitchNodeTest.java index ab446f3215..64559c5125 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbDeviceTypeSwitchNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbDeviceTypeSwitchNodeTest.java @@ -118,7 +118,7 @@ class TbDeviceTypeSwitchNodeTest { } private TbMsg getTbMsg(EntityId entityId) { - return TbMsg.builder() + return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java index c38461d08b..e6d04d6af7 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java @@ -59,7 +59,7 @@ public class TbJsFilterNodeTest { @Test public void falseEvaluationDoNotSendMsg() throws TbNodeException { initWithScript(); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(null) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -79,7 +79,7 @@ public class TbJsFilterNodeTest { public void exceptionInJsThrowsException() throws TbNodeException { initWithScript(); TbMsgMetaData metaData = new TbMsgMetaData(); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(null) .metaData(metaData.copy()) @@ -99,7 +99,7 @@ public class TbJsFilterNodeTest { public void metadataConditionCanBeTrue() throws TbNodeException { initWithScript(); TbMsgMetaData metaData = new TbMsgMetaData(); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(null) .metaData(metaData.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java index 908e941355..981ce15a5f 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java @@ -59,7 +59,7 @@ public class TbJsSwitchNodeTest { metaData.putValue("humidity", "99"); String rawJson = "{\"name\": \"Vit\", \"passed\": 5}"; - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(null) .metaData(metaData.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNodeTest.java index 554b41aebf..4a6b6830f5 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNodeTest.java @@ -97,7 +97,7 @@ class TbMsgTypeFilterNodeTest { } private TbMsg getTbMsg(EntityId entityId, TbMsgType msgType) { - return TbMsg.builder() + return TbMsg.newMsg() .type(msgType) .originator(entityId) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNodeTest.java index 2668588f91..21f6d94829 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNodeTest.java @@ -89,7 +89,7 @@ class TbMsgTypeSwitchNodeTest { } private TbMsg getTbMsg(TbMsgType msgType) { - return TbMsg.builder() + return TbMsg.newMsg() .type(msgType) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNodeTest.java index e3bc8662d7..3246535e15 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNodeTest.java @@ -96,7 +96,7 @@ class TbOriginatorTypeFilterNodeTest { } private TbMsg getTbMsg(EntityId entityId) { - return TbMsg.builder() + return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNodeTest.java index 914e6dfee3..acde991b6e 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNodeTest.java @@ -90,7 +90,7 @@ class TbOriginatorTypeSwitchNodeTest { } private TbMsg getTbMsg(EntityId entityId) { - return TbMsg.builder() + return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbAckNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbAckNodeTest.java index 08b3eff39a..dc166a775b 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbAckNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbAckNodeTest.java @@ -67,7 +67,7 @@ public class TbAckNodeTest { public void givenMsg_whenOnMsg_thenAckAndTellSuccess() throws TbNodeException { node.init(ctxMock, nodeConfiguration); DeviceId deviceId = new DeviceId(UUID.fromString("5770153d-6ca2-4447-8a54-5d8a4538e052")); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeTest.java index 777c93c3f5..d8f5d3aa24 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeTest.java @@ -87,7 +87,7 @@ public class TbCheckpointNodeTest extends AbstractRuleNodeUpgradeTest { given(ctxMock.getQueueName()).willReturn(queueName); node.init(ctxMock, nodeConfiguration); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -106,7 +106,7 @@ public class TbCheckpointNodeTest extends AbstractRuleNodeUpgradeTest { given(ctxMock.getQueueName()).willReturn(DataConstants.HP_QUEUE_NAME); node.init(ctxMock, nodeConfiguration); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainInputNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainInputNodeTest.java index 91e89afd2e..f294f7b7da 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainInputNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainInputNodeTest.java @@ -300,7 +300,7 @@ public class TbRuleChainInputNodeTest extends AbstractRuleNodeUpgradeTest { } private TbMsg getMsg(EntityId entityId) { - return TbMsg.builder() + return TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(entityId) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainOutputNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainOutputNodeTest.java index 623e40c130..aa6e9aeb6f 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainOutputNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainOutputNodeTest.java @@ -74,7 +74,7 @@ public class TbRuleChainOutputNodeTest { node.init(ctxMock, nodeConfiguration); DeviceId deviceId = new DeviceId(UUID.fromString("f514da88-79b3-46da-9f02-1747c5e84f44")); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNodeTest.java index 94e7e36384..d7ae0be329 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNodeTest.java @@ -118,7 +118,7 @@ class TbPubSubNodeTest { given(ctxMock.getExternalCallExecutor()).willReturn(executor); node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(metaData.copy()) @@ -169,7 +169,7 @@ class TbPubSubNodeTest { node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); TbMsgMetaData metadata = new TbMsgMetaData(); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(metadata.copy()) @@ -203,7 +203,7 @@ class TbPubSubNodeTest { node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); TbMsgMetaData metaData = new TbMsgMetaData(); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(metaData.copy()) @@ -236,7 +236,7 @@ class TbPubSubNodeTest { node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); TbMsgMetaData metaData = new TbMsgMetaData(); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(metaData.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeTest.java index cb5c7fa87c..997c9dd7fd 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeTest.java @@ -164,7 +164,7 @@ class TbGpsGeofencingActionNodeTest extends AbstractRuleNodeUpgradeTest { private TbMsg getTbMsg(EntityId entityId, TbMsgMetaData metadata, double latitude, double longitude) { String data = "{\"latitude\": " + latitude + ", \"longitude\": " + longitude + "}"; - return TbMsg.builder() + return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) .metaData(metadata.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeTest.java index fe54dec117..1e3878e015 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeTest.java @@ -448,7 +448,7 @@ class TbGpsGeofencingFilterNodeTest { private TbMsg getTbMsg(EntityId entityId, TbMsgMetaData metadata, double latitude, double longitude) { String data = "{\"latitude\": " + latitude + ", \"longitude\": " + longitude + "}"; - return TbMsg.builder() + return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) .metaData(metadata.copy()) @@ -457,7 +457,7 @@ class TbGpsGeofencingFilterNodeTest { } private TbMsg getEmptyArrayTbMsg(EntityId entityId) { - return TbMsg.builder() + return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeTest.java index 196b2aa8b8..71e896b4f4 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeTest.java @@ -188,7 +188,7 @@ public class TbKafkaNodeTest { ReflectionTestUtils.setField(node, "initError", new ThingsboardKafkaClientError(errorMsg)); // WHEN - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -217,7 +217,7 @@ public class TbKafkaNodeTest { // WHEN node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -242,7 +242,7 @@ public class TbKafkaNodeTest { // GIVEN config.setTopicPattern(topicPattern); config.setKeyPattern(keyPattern); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(metaData.copy()) @@ -293,7 +293,7 @@ public class TbKafkaNodeTest { // WHEN node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -323,7 +323,7 @@ public class TbKafkaNodeTest { // WHEN node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -356,7 +356,7 @@ public class TbKafkaNodeTest { node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("key", "value"); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(metaData.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeTest.java index 4036dfd6b0..eaa0d00a25 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeTest.java @@ -103,7 +103,7 @@ public class TbMsgToEmailNodeTest { } var msgDataStr = "{\"temperature\": " + EXPECTED_TEMPERATURE + "}"; - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) .metaData(md.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java index 17256cfac0..1a8e8179a9 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java @@ -173,7 +173,7 @@ public class TbMathNodeTest { metaData.putValue("key2", "argumentA"); ObjectNode msgNode = JacksonUtil.newObjectNode() .put("key3", "argumentB").put("argumentA", 2).put("argumentB", 2); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) .metaData(metaData.copy()) @@ -186,7 +186,7 @@ public class TbMathNodeTest { metaData.putValue("key2", "argumentC"); msgNode = JacksonUtil.newObjectNode() .put("key3", "argumentD").put("argumentC", 4).put("argumentD", 3); - msg = TbMsg.builder() + msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) .metaData(metaData.copy()) @@ -238,7 +238,7 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "b") ); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -306,7 +306,7 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -334,7 +334,7 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "b") ); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -362,7 +362,7 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "b") ); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -391,7 +391,7 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.TIME_SERIES, "b") ); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -424,7 +424,7 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -451,7 +451,7 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -478,7 +478,7 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -509,7 +509,7 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -539,7 +539,7 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -575,7 +575,7 @@ public class TbMathNodeTest { new TbMathResult(TbMathArgumentType.MESSAGE_METADATA, "result", 3, false, false, null), tbMathArgument ); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -600,7 +600,7 @@ public class TbMathNodeTest { new TbMathResult(TbMathArgumentType.TIME_SERIES, "result", 3, true, false, DataConstants.SERVER_SCOPE), new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "TestKey") ); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -620,7 +620,7 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -645,7 +645,7 @@ public class TbMathNodeTest { CountDownLatch slowProcessingLatch = new CountDownLatch(1); List slowMsgList = IntStream.range(0, 5) - .mapToObj(x -> TbMsg.builder() + .mapToObj(x -> TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originatorSlow) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -653,7 +653,7 @@ public class TbMathNodeTest { .build()) .toList(); List fastMsgList = IntStream.range(0, 2) - .mapToObj(x -> TbMsg.builder() + .mapToObj(x -> TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originatorFast) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -725,7 +725,7 @@ public class TbMathNodeTest { CountDownLatch slowProcessingLatch = new CountDownLatch(1); List slowMsgList = IntStream.range(0, 5) - .mapToObj(x -> TbMsg.builder() + .mapToObj(x -> TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originatorSlow) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -803,7 +803,7 @@ public class TbMathNodeTest { }) .toList(); ctxNodes.forEach(ctxNode -> ruleEngineDispatcherExecutor.executeAsync(() -> ctxNode.getRight() - .onMsg(ctxNode.getLeft(), TbMsg.builder() + .onMsg(ctxNode.getLeft(), TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeTest.java index c5a0ba4314..bfb2b46037 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeTest.java @@ -169,7 +169,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { // GIVEN node.init(ctxMock, nodeConfiguration); var msgData = "{\"pulseCounter\": 42}"; - var msg = TbMsg.builder() + var msg = TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -189,7 +189,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { public void givenInvalidMsgDataType_whenOnMsg_thenShouldTellNextOther() throws TbNodeException { // GIVEN node.init(ctxMock, nodeConfiguration); - var msg = TbMsg.builder() + var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -210,7 +210,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { public void givenInputKeyIsNotPresent_whenOnMsg_thenShouldTellNextOther() throws TbNodeException { // GIVEN node.init(ctxMock, nodeConfiguration); - var msg = TbMsg.builder() + var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -239,7 +239,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new DoubleDataEntry("temperature", 40.5))); var msgData = "{\"temperature\": 42,\"airPressure\":123}"; - var msg = TbMsg.builder() + var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -274,7 +274,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry("temperature", 40L))); var msgData = "{\"temperature\": 42,\"airPressure\":123}"; - var msg = TbMsg.builder() + var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -309,7 +309,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry("temperature", "40.0"))); var msgData = "{\"temperature\": 42,\"airPressure\":123}"; - var msg = TbMsg.builder() + var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -348,7 +348,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { var msgData = "{\"temperature\": 42,\"airPressure\":123}"; var firstMsgMetaData = new TbMsgMetaData(); firstMsgMetaData.putValue("ts", String.valueOf(3L)); - var firstMsg = TbMsg.builder() + var firstMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(firstMsgMetaData.copy()) @@ -379,7 +379,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { var secondMsgMetaData = new TbMsgMetaData(); secondMsgMetaData.putValue("ts", String.valueOf(6L)); - var secondMsg = TbMsg.builder() + var secondMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(secondMsgMetaData.copy()) @@ -415,7 +415,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new DoubleDataEntry("temperature", null))); var msgData = "{\"temperature\": 42,\"airPressure\":123}"; - var msg = TbMsg.builder() + var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -448,7 +448,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry("pulseCounter", 200L))); var msgData = "{\"pulseCounter\":\"123\"}"; - var msg = TbMsg.builder() + var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -485,7 +485,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry("pulseCounter", 200L))); var msgData = "{\"pulseCounter\":\"123\"}"; - var msg = TbMsg.builder() + var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -514,7 +514,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry("pulseCounter", "high"))); var msgData = "{\"pulseCounter\":\"123\"}"; - var msg = TbMsg.builder() + var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -544,7 +544,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new BooleanDataEntry("pulseCounter", false))); var msgData = "{\"pulseCounter\":true}"; - var msg = TbMsg.builder() + var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -574,7 +574,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new JsonDataEntry("pulseCounter", "{\"isActive\":false}"))); var msgData = "{\"pulseCounter\":{\"isActive\":true}}"; - var msg = TbMsg.builder() + var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -622,7 +622,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { List tbMsgList = IntStream.range(0, RULE_DISPATCHER_POOL_SIZE * 2).mapToObj(x -> { var msgData = "{\"pulseCounter\":" + 2 + "}"; - return TbMsg.builder() + return TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -672,7 +672,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { mockFindLatestAsync(new BasicTsKvEntry(1L, new DoubleDataEntry("temperature", testConfig.prevValue()))); var msgData = "{\"temperature\":" + testConfig.currentValue() + ",\"airPressure\":123}"; - var msg = TbMsg.builder() + var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNodeTest.java index 725bb10b12..c11dcfd749 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNodeTest.java @@ -173,7 +173,7 @@ public class TbFetchDeviceCredentialsNodeTest { final var metaData = new TbMsgMetaData(mdMap); final String data = "{\"TestAttribute_1\": \"humidity\", \"TestAttribute_2\": \"voltage\"}"; - return TbMsg.builder() + return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) .metaData(metaData.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNodeTest.java index 84267c772b..1f5770bf52 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNodeTest.java @@ -246,7 +246,7 @@ public class TbGetAttributesNodeTest extends AbstractRuleNodeUpgradeTest { public void givenFetchLatestTimeseriesToDataAndDataIsNotJsonObject_whenOnMsg_thenException() throws Exception { // GIVEN node = initNode(TbMsgSource.DATA, true, true); - var msg = TbMsg.builder() + var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(ORIGINATOR_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -347,7 +347,7 @@ public class TbGetAttributesNodeTest extends AbstractRuleNodeUpgradeTest { msgMetaData.putValue("client_attr_metadata", "client_attr_3"); msgMetaData.putValue("server_attr_metadata", "server_attr_3"); - return TbMsg.builder() + return TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(entityId) .metaData(msgMetaData.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java index 6a682679f4..c5ce45a996 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java @@ -210,7 +210,7 @@ public class TbGetCustomerAttributeNodeTest { public void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException() { // GIVEN node.fetchTo = TbMsgSource.DATA; - msg = TbMsg.builder() + msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -230,7 +230,7 @@ public class TbGetCustomerAttributeNodeTest { // GIVEN var userId = new UserId(UUID.randomUUID()); - msg = TbMsg.builder() + msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(userId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -479,7 +479,7 @@ public class TbGetCustomerAttributeNodeTest { var msgData = "{\"temp\":42,\"humidity\":77,\"messageBodyPattern1\":\"targetKey2\",\"messageBodyPattern2\":\"sourceKey3\"}"; - msg = TbMsg.builder() + msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) .metaData(msgMetaData.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeTest.java index 0586b84b75..31cb6a7083 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeTest.java @@ -159,7 +159,7 @@ public class TbGetCustomerDetailsNodeTest { public void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException() { // GIVEN node.fetchTo = TbMsgSource.DATA; - msg = TbMsg.builder() + msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -463,7 +463,7 @@ public class TbGetCustomerDetailsNodeTest { var msgData = "{\"dataKey1\":123,\"dataKey2\":\"dataValue2\"}"; - msg = TbMsg.builder() + msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) .metaData(msgMetaData.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNodeTest.java index af93181bfb..fb21fe3737 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNodeTest.java @@ -115,7 +115,7 @@ public class TbGetDeviceAttrNodeTest extends AbstractRuleNodeUpgradeTest { given(deviceServiceMock.findDevicesByQuery(any(TenantId.class), any(DeviceSearchQuery.class))).willReturn(Futures.immediateFuture(Collections.emptyList())); given(ctxMock.getDbCallbackExecutor()).willReturn(executor); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNodeTest.java index d67d00416e..75a0bf4868 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNodeTest.java @@ -135,7 +135,7 @@ public class TbGetOriginatorFieldsNodeTest { public void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException() { // GIVEN node.fetchTo = TbMsgSource.DATA; - msg = TbMsg.builder() + msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -169,7 +169,7 @@ public class TbGetOriginatorFieldsNodeTest { node.fetchTo = TbMsgSource.DATA; var msgMetaData = new TbMsgMetaData(); var msgData = "{\"temp\":42,\"humidity\":77}"; - msg = TbMsg.builder() + msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(msgMetaData.copy()) @@ -216,7 +216,7 @@ public class TbGetOriginatorFieldsNodeTest { node.fetchTo = TbMsgSource.DATA; var msgMetaData = new TbMsgMetaData(); var msgData = "{\"temp\":42,\"humidity\":77}"; - msg = TbMsg.builder() + msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(msgMetaData.copy()) @@ -264,7 +264,7 @@ public class TbGetOriginatorFieldsNodeTest { "testKey1", "testValue1", "testKey2", "123")); var msgData = "[\"value1\",\"value2\"]"; - msg = TbMsg.builder() + msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(msgMetaData.copy()) @@ -317,7 +317,7 @@ public class TbGetOriginatorFieldsNodeTest { "testKey1", "testValue1", "testKey2", "123")); var msgData = "[\"value1\",\"value2\"]"; - msg = TbMsg.builder() + msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(msgMetaData.copy()) @@ -380,7 +380,7 @@ public class TbGetOriginatorFieldsNodeTest { "testKey1", "testValue1", "testKey2", "123")); var msgData = "[\"value1\",\"value2\"]"; - msg = TbMsg.builder() + msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(new DashboardId(UUID.randomUUID())) .metaData(msgMetaData.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java index f97700e435..83a9d4afbf 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java @@ -223,7 +223,7 @@ public class TbGetRelatedAttributeNodeTest { public void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException() { // GIVEN node.fetchTo = TbMsgSource.DATA; - msg = TbMsg.builder() + msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -597,7 +597,7 @@ public class TbGetRelatedAttributeNodeTest { msgData = "{\"temp\":42,\"humidity\":77,\"messageBodyPattern1\":\"targetKey2\",\"messageBodyPattern2\":\"sourceKey3\"}"; } - msg = TbMsg.builder() + msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) .metaData(msgMetaData.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNodeTest.java index c27f5450f3..63044b311b 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNodeTest.java @@ -182,7 +182,7 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { // WHEN-THEN node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -210,7 +210,7 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { long endTs = 1719220353000L; TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("mdStartInterval", String.valueOf(startTs)); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(metaData.copy()) @@ -237,7 +237,7 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { given(timeseriesServiceMock.findAll(any(TenantId.class), any(EntityId.class), anyList())).willReturn(Futures.immediateFuture(Collections.emptyList())); // WHEN - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -266,7 +266,7 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { // WHEN TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("mdTsKey", "humidity"); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(metaData.copy()) @@ -296,7 +296,7 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { given(timeseriesServiceMock.findAll(any(TenantId.class), any(EntityId.class), anyList())).willReturn(Futures.immediateFuture(Collections.emptyList())); // WHEN - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -331,7 +331,7 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { given(timeseriesServiceMock.findAll(any(TenantId.class), any(EntityId.class), anyList())).willReturn(Futures.immediateFuture(Collections.emptyList())); // WHEN - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -376,7 +376,7 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { given(timeseriesServiceMock.findAll(any(TenantId.class), any(EntityId.class), anyList())).willReturn(Futures.immediateFuture(Collections.emptyList())); // WHEN - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -417,7 +417,7 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); // WHEN-THEN - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -451,7 +451,7 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { given(timeseriesServiceMock.findAll(any(TenantId.class), any(EntityId.class), anyList())).willReturn(Futures.immediateFuture(tsKvEntries)); // WHEN - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -487,7 +487,7 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { given(timeseriesServiceMock.findAll(any(TenantId.class), any(EntityId.class), anyList())).willReturn(Futures.immediateFuture(tsKvEntries)); // WHEN - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java index a7ba3f54ed..5ad0163a7c 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java @@ -190,7 +190,7 @@ public class TbGetTenantAttributeNodeTest { public void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException() { // GIVEN node.fetchTo = TbMsgSource.DATA; - msg = TbMsg.builder() + msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -403,7 +403,7 @@ public class TbGetTenantAttributeNodeTest { var msgData = "{\"temp\":42,\"humidity\":77,\"messageBodyPattern1\":\"targetKey2\",\"messageBodyPattern2\":\"sourceKey3\"}"; - msg = TbMsg.builder() + msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) .metaData(msgMetaData.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeTest.java index b030ae75aa..7cf9ddcdc8 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeTest.java @@ -129,7 +129,7 @@ public class TbGetTenantDetailsNodeTest { public void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException() { // GIVEN node.fetchTo = TbMsgSource.DATA; - msg = TbMsg.builder() + msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -294,7 +294,7 @@ public class TbGetTenantDetailsNodeTest { var msgData = "{\"dataKey1\":123,\"dataKey2\":\"dataValue2\"}"; - msg = TbMsg.builder() + msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) .metaData(msgMetaData.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeTest.java index 60c6eebb5b..6b074da45a 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeTest.java @@ -283,7 +283,7 @@ public class TbMqttNodeTest extends AbstractRuleNodeUpgradeTest { return null; }).given(future).addListener(any()); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(metaData.copy()) @@ -327,7 +327,7 @@ public class TbMqttNodeTest extends AbstractRuleNodeUpgradeTest { return null; }).given(future).addListener(any()); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/DeviceStateTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/DeviceStateTest.java index 973a426513..897a332c95 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/DeviceStateTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/DeviceStateTest.java @@ -95,7 +95,7 @@ public class DeviceStateTest { when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), any())).thenAnswer(invocationOnMock -> { TbMsgType type = invocationOnMock.getArgument(1); String data = invocationOnMock.getArgument(invocationOnMock.getArguments().length - 1); - return TbMsg.builder() + return TbMsg.newMsg() .type(type) .originator(null) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -112,7 +112,7 @@ public class DeviceStateTest { DeviceId deviceId = new DeviceId(UUID.randomUUID()); DeviceState deviceState = createDeviceState(deviceId, alarmConfig); - TbMsg attributeUpdateMsg = TbMsg.builder() + TbMsg attributeUpdateMsg = TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -125,7 +125,7 @@ public class DeviceStateTest { verify(ctx).enqueueForTellNext(resultMsgCaptor.capture(), eq("Alarm Created")); Alarm alarm = JacksonUtil.fromString(resultMsgCaptor.getValue().getData(), Alarm.class); - deviceState.process(ctx, TbMsg.builder() + deviceState.process(ctx, TbMsg.newMsg() .type(TbMsgType.ALARM_CLEAR) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -134,7 +134,7 @@ public class DeviceStateTest { reset(ctx); String deletedAttributes = "{ \"attributes\": [ \"other\" ] }"; - deviceState.process(ctx, TbMsg.builder() + deviceState.process(ctx, TbMsg.newMsg() .type(TbMsgType.ATTRIBUTES_DELETED) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -149,7 +149,7 @@ public class DeviceStateTest { DeviceId deviceId = new DeviceId(UUID.randomUUID()); DeviceState deviceState = createDeviceState(deviceId, alarmConfig); - TbMsg attributeUpdateMsg = TbMsg.builder() + TbMsg attributeUpdateMsg = TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -161,14 +161,14 @@ public class DeviceStateTest { verify(ctx).enqueueForTellNext(resultMsgCaptor.capture(), eq("Alarm Created")); Alarm alarm = JacksonUtil.fromString(resultMsgCaptor.getValue().getData(), Alarm.class); - deviceState.process(ctx, TbMsg.builder() + deviceState.process(ctx, TbMsg.newMsg() .type(TbMsgType.ALARM_CLEAR) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) .data(JacksonUtil.toString(alarm)) .build()); - TbMsg alarmDeleteNotification = TbMsg.builder() + TbMsg alarmDeleteNotification = TbMsg.newMsg() .type(TbMsgType.ALARM_DELETE) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java index afc5401d89..01e42f042e 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java @@ -127,7 +127,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 42); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type("123456789") .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -151,7 +151,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 42); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -210,7 +210,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")).thenReturn(null); registerCreateAlarmMock(alarmService.createAlarm(any()), true); - TbMsg theMsg = TbMsg.builder() + TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -220,7 +220,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 42); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -234,7 +234,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { verify(ctx).enqueueForTellNext(theMsg, "Alarm Created"); verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); - TbMsg theMsg2 = TbMsg.builder() + TbMsg theMsg2 = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -245,7 +245,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { registerCreateAlarmMock(alarmService.updateAlarm(any()), false); Thread.sleep(1); - TbMsg msg2 = TbMsg.builder() + TbMsg msg2 = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -310,7 +310,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm1")).thenReturn(null); registerCreateAlarmMock(alarmService.createAlarm(any()), true); - TbMsg theMsg = TbMsg.builder() + TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -320,7 +320,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 42); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -334,7 +334,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { verify(ctx).enqueueForTellNext(theMsg, "Alarm Created"); verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); - TbMsg theMsg2 = TbMsg.builder() + TbMsg theMsg2 = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -358,7 +358,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { when(alarmService.updateAlarm(any())).thenReturn(result); data.put("temperature", 52); - TbMsg msg2 = TbMsg.builder() + TbMsg msg2 = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -440,7 +440,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(attrListListenableFuture); - TbMsg theMsg = TbMsg.builder() + TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -451,7 +451,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 21); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -540,7 +540,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), Mockito.any(AttributeScope.class), Mockito.anyString())) .thenReturn(attrListListenableFuture); - TbMsg theMsg = TbMsg.builder() + TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -551,7 +551,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 21); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -622,7 +622,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listListenableFutureWithLess); - TbMsg theMsg = TbMsg.builder() + TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -633,7 +633,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 35); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -730,7 +730,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listListenableFuture); - TbMsg theMsg = TbMsg.builder() + TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -741,7 +741,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 35); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -763,7 +763,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Thread.sleep(halfOfAlarmDelay + 1); - TbMsg msg2 = TbMsg.builder() + TbMsg msg2 = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -875,7 +875,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listNoDurationAttribute); - TbMsg theMsg = TbMsg.builder() + TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -886,7 +886,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 150); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -908,7 +908,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Thread.sleep(halfOfAlarmDelay + 1); - TbMsg msg2 = TbMsg.builder() + TbMsg msg2 = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -1005,7 +1005,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listListenableFuture); - TbMsg theMsg = TbMsg.builder() + TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -1016,7 +1016,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 150); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -1032,7 +1032,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { verify(ctx, Mockito.never()).tellNext(theMsg, "Alarm Created"); data.put("temperature", 151); - TbMsg msg2 = TbMsg.builder() + TbMsg msg2 = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -1142,7 +1142,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listNoDurationAttribute); - TbMsg theMsg = TbMsg.builder() + TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -1153,7 +1153,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 150); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -1169,7 +1169,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { verify(ctx, Mockito.never()).tellNext(theMsg, "Alarm Created"); data.put("temperature", 151); - TbMsg msg2 = TbMsg.builder() + TbMsg msg2 = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -1258,7 +1258,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listListenableFuture); - TbMsg theMsg = TbMsg.builder() + TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -1269,7 +1269,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 35); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -1291,7 +1291,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Thread.sleep(halfOfAlarmDelay + 1); - TbMsg msg2 = TbMsg.builder() + TbMsg msg2 = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -1376,7 +1376,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listListenableFuture); - TbMsg theMsg = TbMsg.builder() + TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -1387,7 +1387,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 35); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -1471,7 +1471,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listListenableFutureActiveSchedule); - TbMsg theMsg = TbMsg.builder() + TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -1482,7 +1482,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 35); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -1580,7 +1580,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listListenableFutureInactiveSchedule); - TbMsg theMsg = TbMsg.builder() + TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -1589,7 +1589,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 35); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -1671,7 +1671,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(customerId), eq(AttributeScope.SERVER_SCOPE), Mockito.anyString())) .thenReturn(optionalListenableFutureWithLess); - TbMsg theMsg = TbMsg.builder() + TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -1682,7 +1682,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 25); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -1757,7 +1757,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(AttributeScope.SERVER_SCOPE), Mockito.anyString())) .thenReturn(optionalListenableFutureWithLess); - TbMsg theMsg = TbMsg.builder() + TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -1768,7 +1768,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 40); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -1853,7 +1853,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(AttributeScope.SERVER_SCOPE), Mockito.anyString())) .thenReturn(optionalListenableFutureWithLess); - TbMsg theMsg = TbMsg.builder() + TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -1864,7 +1864,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 150L); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -1951,7 +1951,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(AttributeScope.SERVER_SCOPE), Mockito.anyString())) .thenReturn(optionalListenableFutureWithLess); - TbMsg theMsg = TbMsg.builder() + TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -1962,7 +1962,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 150L); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java index 91a9055c37..24d3a51cd8 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java @@ -147,7 +147,7 @@ public class TbRabbitMqNodeTest { given(ctxMock.getExternalCallExecutor()).willReturn(executor); node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(metaData.copy()) @@ -183,7 +183,7 @@ public class TbRabbitMqNodeTest { given(ctxMock.getExternalCallExecutor()).willReturn(executor); node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -211,7 +211,7 @@ public class TbRabbitMqNodeTest { node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); TbMsgMetaData metaData = new TbMsgMetaData(); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(metaData.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbHttpClientTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbHttpClientTest.java index cc883db254..be4f3488ec 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbHttpClientTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbHttpClientTest.java @@ -140,13 +140,13 @@ public class TbHttpClientTest { var httpClient = new TbHttpClient(config, eventLoop); - var msg = TbMsg.builder() + var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(new DeviceId(EntityId.NULL_UUID)) .metaData(TbMsgMetaData.EMPTY.copy()) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); - var successMsg = TbMsg.builder() + var successMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(msg.getOriginator()) .metaData(msg.getMetaData().copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java index 9f19fe019b..d94981cff3 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java @@ -142,7 +142,7 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest { config.setRestEndpointUrlPattern(String.format("http://localhost:%d%s", server.getLocalPort(), path)); initWithConfig(config); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) .metaData(metaData.copy()) @@ -211,7 +211,7 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest { config.setRestEndpointUrlPattern(String.format("http://localhost:%d%s", server.getLocalPort(), path)); initWithConfig(config); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) .metaData(metaData.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbSendRestApiCallReplyNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbSendRestApiCallReplyNodeTest.java index 7ca88da0a3..e7613a11cf 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbSendRestApiCallReplyNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbSendRestApiCallReplyNodeTest.java @@ -89,7 +89,7 @@ public class TbSendRestApiCallReplyNodeTest { Map metadata = Map.of( requestIdAttribute, requestUUIDStr, serviceIdAttribute, serviceIdStr); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.REST_API_REQUEST) .originator(DEVICE_ID) .metaData(new TbMsgMetaData(metadata).copy()) @@ -114,7 +114,7 @@ public class TbSendRestApiCallReplyNodeTest { @ParameterizedTest @MethodSource public void givenInvalidRequest_whenOnMsg_thenTellFailure(TbMsgMetaData metaData, String data, String errorMsg) { - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.REST_API_REQUEST) .originator(DEVICE_ID) .metaData(metaData.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java index c5bab86d8c..61e2b45b42 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java @@ -92,7 +92,7 @@ public class TbSendRPCReplyNodeTest { public void sendReplyToTransport() { when(ctx.getRpcService()).thenReturn(rpcService); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(getDefaultMetadata().copy()) @@ -118,7 +118,7 @@ public class TbSendRPCReplyNodeTest { TbMsgMetaData defaultMetadata = getDefaultMetadata(); defaultMetadata.putValue(DataConstants.EDGE_ID, UUID.randomUUID().toString()); defaultMetadata.putValue(DataConstants.DEVICE_ID, UUID.randomUUID().toString()); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(defaultMetadata.copy()) @@ -138,7 +138,7 @@ public class TbSendRPCReplyNodeTest { @EnumSource(EntityType.class) public void testOriginatorEntityTypes(EntityType entityType) { EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, "0f386739-210f-4e23-8739-23f84a172adc"); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(entityId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -157,7 +157,7 @@ public class TbSendRPCReplyNodeTest { @ParameterizedTest @MethodSource public void testForAvailabilityOfMetadataAndDataValues(TbMsgMetaData metaData, String errorMsg) { - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(metaData.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java index 322d334c31..a22554fd56 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java @@ -107,7 +107,7 @@ public class TbSendRPCRequestNodeTest { TbMsgMetaData msgMetadata = new TbMsgMetaData(); msgMetadata.putValue("oneway", mdKeyValue); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) .metaData(msgMetadata.copy()) @@ -133,7 +133,7 @@ public class TbSendRPCRequestNodeTest { given(ctxMock.getRpcService()).willReturn(rpcServiceMock); given(ctxMock.getTenantId()).willReturn(TENANT_ID); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -159,7 +159,7 @@ public class TbSendRPCRequestNodeTest { given(ctxMock.getRpcService()).willReturn(rpcServiceMock); given(ctxMock.getTenantId()).willReturn(TENANT_ID); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.TO_SERVER_RPC_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -185,7 +185,7 @@ public class TbSendRPCRequestNodeTest { "requestId": 12345 } """; - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.TO_SERVER_RPC_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -205,7 +205,7 @@ public class TbSendRPCRequestNodeTest { String requestUUID = "b795a241-5a30-48fb-92d5-46b864d47130"; TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue("requestUUID", requestUUID); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) .metaData(metadata.copy()) @@ -225,7 +225,7 @@ public class TbSendRPCRequestNodeTest { TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue("requestUUID", requestUUID); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) .metaData(metadata.copy()) @@ -245,7 +245,7 @@ public class TbSendRPCRequestNodeTest { String originServiceId = "service-id-123"; TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue("originServiceId", originServiceId); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) .metaData(metadata.copy()) @@ -265,7 +265,7 @@ public class TbSendRPCRequestNodeTest { TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue("originServiceId", originServiceId); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) .metaData(metadata.copy()) @@ -285,7 +285,7 @@ public class TbSendRPCRequestNodeTest { String expirationTime = "2000000000000"; TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue(DataConstants.EXPIRATION_TIME, expirationTime); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) .metaData(metadata.copy()) @@ -305,7 +305,7 @@ public class TbSendRPCRequestNodeTest { TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue(DataConstants.EXPIRATION_TIME, expirationTime); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) .metaData(metadata.copy()) @@ -325,7 +325,7 @@ public class TbSendRPCRequestNodeTest { Integer retries = 3; TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue(DataConstants.RETRIES, String.valueOf(retries)); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) .metaData(metadata.copy()) @@ -345,7 +345,7 @@ public class TbSendRPCRequestNodeTest { TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue(DataConstants.RETRIES, retries); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) .metaData(metadata.copy()) @@ -363,7 +363,7 @@ public class TbSendRPCRequestNodeTest { given(ctxMock.getRpcService()).willReturn(rpcServiceMock); given(ctxMock.getTenantId()).willReturn(TENANT_ID); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(msgType) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -387,7 +387,7 @@ public class TbSendRPCRequestNodeTest { TbMsgMetaData metadata = new TbMsgMetaData(); metadata.putValue(DataConstants.PERSISTENT, isPersisted); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) .metaData(metadata.copy()) @@ -416,7 +416,7 @@ public class TbSendRPCRequestNodeTest { @Test public void givenRpcResponseWithoutError_whenOnMsg_thenSendsRpcRequest() { - TbMsg outMsg = TbMsg.builder() + TbMsg outMsg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -436,7 +436,7 @@ public class TbSendRPCRequestNodeTest { return null; }).given(rpcServiceMock).sendRpcRequestToDevice(any(RuleEngineDeviceRpcRequest.class), any(Consumer.class)); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -450,7 +450,7 @@ public class TbSendRPCRequestNodeTest { @Test public void givenRpcResponseWithError_whenOnMsg_thenTellFailure() { - TbMsg outMsg = TbMsg.builder() + TbMsg outMsg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -469,7 +469,7 @@ public class TbSendRPCRequestNodeTest { return null; }).given(rpcServiceMock).sendRpcRequestToDevice(any(RuleEngineDeviceRpcRequest.class), any(Consumer.class)); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -486,7 +486,7 @@ public class TbSendRPCRequestNodeTest { public void givenOriginatorIsNotDevice_whenOnMsg_thenThrowsException(EntityType entityType) { EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, "ac21a1bb-eabf-4463-8313-24bea1f498d9"); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(entityId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -504,7 +504,7 @@ public class TbSendRPCRequestNodeTest { @ParameterizedTest @ValueSource(strings = {"method", "params"}) public void givenMethodOrParamsAreNotPresent_whenOnMsg_thenThrowsException(String key) { - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java index 4e1add54fe..470a397ea4 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java @@ -164,7 +164,7 @@ class TbMsgAttributesNodeTest extends AbstractRuleNodeUpgradeTest { md.putValue(NOTIFY_DEVICE_METADATA_KEY, mdValue); } // dummy list with one ts kv to pass the empty list check. - var testTbMsg = TbMsg.builder() + var testTbMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) .metaData(md.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNodeTest.java index 680e8b5004..80259b34c0 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNodeTest.java @@ -139,7 +139,7 @@ public class TbMsgDeleteAttributesNodeTest { } final String data = "{\"TestAttribute_2\": \"humidity\", \"TestAttribute_3\": \"voltage\"}"; - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(deviceId) .metaData(metaData.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java index 0a7cbc7307..3bd7a67794 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java @@ -96,7 +96,7 @@ public class TbMsgTimeseriesNodeTest { @EnumSource(TbMsgType.class) public void givenMsgTypeAndEmptyMsgData_whenOnMsg_thenVerifyFailureMsg(TbMsgType msgType) throws TbNodeException { init(); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(msgType) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -127,7 +127,7 @@ public class TbMsgTimeseriesNodeTest { "humidity": 77 } """; - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -169,7 +169,7 @@ public class TbMsgTimeseriesNodeTest { """; long ts = System.currentTimeMillis(); var metadata = Map.of("ts", String.valueOf(ts)); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(new TbMsgMetaData(metadata).copy()) @@ -212,7 +212,7 @@ public class TbMsgTimeseriesNodeTest { """; var metadata = new TbMsgMetaData(); metadata.putValue("TTL", ttlFromMd); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(metadata.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java index dd0b3e3385..455394e13f 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java @@ -176,7 +176,7 @@ public class TbChangeOriginatorNodeTest { Device device = new Device(DEVICE_ID); device.setCustomerId(CUSTOMER_ID); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -204,7 +204,7 @@ public class TbChangeOriginatorNodeTest { public void givenOriginatorSourceIsTenant_whenOnMsg_thenTellSuccess() throws TbNodeException { config.setOriginatorSource(TENANT); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(ASSET_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -229,7 +229,7 @@ public class TbChangeOriginatorNodeTest { public void givenOriginatorSourceIsRelatedAndNewOriginatorIsNull_whenOnMsg_thenTellFailure() throws TbNodeException { config.setOriginatorSource(RELATED); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(ASSET_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -268,7 +268,7 @@ public class TbChangeOriginatorNodeTest { Alarm alarm = new Alarm(alarmId); alarm.setOriginator(DEVICE_ID); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(alarmId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -299,7 +299,7 @@ public class TbChangeOriginatorNodeTest { config.setEntityType(EntityType.ASSET.name()); config.setEntityNamePattern(entityNamePattern); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(metaData.copy()) @@ -340,7 +340,7 @@ public class TbChangeOriginatorNodeTest { TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("md-name-pattern", "test-asset"); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(metaData.copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeTest.java index fe27c4452b..427181c5af 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeTest.java @@ -196,7 +196,7 @@ public class TbCopyKeysNodeTest { "voltageDataValue", "220", "city", "NY" ); - return TbMsg.builder() + return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) .metaData(new TbMsgMetaData(mdMap).copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNodeTest.java index c47beffdec..93b34182a8 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNodeTest.java @@ -173,7 +173,7 @@ public class TbDeleteKeysNodeTest { "voltageDataValue", "220", "city", "NY" ); - return TbMsg.builder() + return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) .metaData(new TbMsgMetaData(mdMap).copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbJsonPathNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbJsonPathNodeTest.java index 3ca1521f43..298df82d2a 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbJsonPathNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbJsonPathNodeTest.java @@ -171,7 +171,7 @@ public class TbJsonPathNodeTest { Map mdMap = Map.of("country", "US", "city", "NY" ); - return TbMsg.builder() + return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) .metaData(new TbMsgMetaData(mdMap).copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbMsgDeduplicationNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbMsgDeduplicationNodeTest.java index 1a79865867..84b648823f 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbMsgDeduplicationNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbMsgDeduplicationNodeTest.java @@ -103,7 +103,7 @@ public class TbMsgDeduplicationNodeTest extends AbstractRuleNodeUpgradeTest { EntityId originator = (EntityId) (invocationOnMock.getArguments())[2]; TbMsgMetaData metaData = (TbMsgMetaData) (invocationOnMock.getArguments())[3]; String data = (String) (invocationOnMock.getArguments())[4]; - return TbMsg.builder() + return TbMsg.newMsg() .type(type) .originator(originator) .metaData(metaData.copy().copy()) @@ -457,7 +457,7 @@ public class TbMsgDeduplicationNodeTest extends AbstractRuleNodeUpgradeTest { dataNode.put("deviceId", deviceId.getId().toString()); TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("ts", String.valueOf(ts)); - return TbMsg.builder() + return TbMsg.newMsg() .queueName(DataConstants.MAIN_QUEUE_NAME) .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeTest.java index 851d45edc7..61851945c0 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeTest.java @@ -188,7 +188,7 @@ public class TbRenameKeysNodeTest { "country", "US", "city", "NY" ); - return TbMsg.builder() + return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) .metaData(new TbMsgMetaData(mdMap).copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNodeTest.java index 1ba7760244..b811934718 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNodeTest.java @@ -132,7 +132,7 @@ public class TbSplitArrayMsgNodeTest { "country", "US", "city", "NY" ); - return TbMsg.builder() + return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) .metaData(new TbMsgMetaData(mdMap).copy()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java index 5c8ea2c0f3..094cc4ac18 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java @@ -61,7 +61,7 @@ public class TbTransformMsgNodeTest { RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased()); RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(null) .metaData(metaData.copy()) @@ -70,7 +70,7 @@ public class TbTransformMsgNodeTest { .ruleChainId(ruleChainId) .ruleNodeId(ruleNodeId) .build(); - TbMsg transformedMsg = TbMsg.builder() + TbMsg transformedMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(null) .metaData(metaData.copy()) @@ -97,7 +97,7 @@ public class TbTransformMsgNodeTest { RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased()); RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(null) .metaData(metaData.copy()) From 0b3ffb5b4a090ee45f2325160313d43f03e0d2b6 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 13 Dec 2024 14:21:55 +0200 Subject: [PATCH 009/108] Refactor TbMsg transforming --- .../actors/ruleChain/DefaultTbContext.java | 23 +- .../queue/DefaultTbClusterService.java | 11 +- .../script/RuleNodeJsScriptEngine.java | 6 +- .../script/RuleNodeTbelScriptEngine.java | 6 +- .../queue/DefaultTbClusterServiceTest.java | 2 +- .../thingsboard/server/common/msg/TbMsg.java | 215 ++++++++---------- .../engine/action/TbAbstractAlarmNode.java | 4 +- .../engine/aws/lambda/TbAwsLambdaNode.java | 9 +- .../rule/engine/aws/sns/TbSnsNode.java | 8 +- .../rule/engine/aws/sqs/TbSqsNode.java | 8 +- .../rule/engine/gcp/pubsub/TbPubSubNode.java | 8 +- .../rule/engine/kafka/TbKafkaNode.java | 8 +- .../rule/engine/math/TbMathNode.java | 8 +- .../engine/metadata/CalculateDeltaNode.java | 4 +- .../metadata/TbAbstractNodeWithFetchTo.java | 8 +- .../engine/metadata/TbGetTelemetryNode.java | 4 +- .../rule/engine/mqtt/TbMqttNode.java | 4 +- .../notification/TbNotificationNode.java | 4 +- .../rule/engine/rabbitmq/TbRabbitMqNode.java | 4 +- .../rule/engine/rest/TbHttpClient.java | 8 +- .../rule/engine/transform/TbCopyKeysNode.java | 5 +- .../engine/transform/TbDeleteKeysNode.java | 5 +- .../rule/engine/transform/TbJsonPathNode.java | 4 +- .../engine/transform/TbRenameKeysNode.java | 5 +- .../engine/transform/TbSplitArrayMsgNode.java | 8 +- .../engine/action/TbCreateAlarmNodeTest.java | 72 +++--- .../action/TbCreateRelationNodeTest.java | 4 +- .../aws/lambda/TbAwsLambdaNodeTest.java | 18 +- .../engine/gcp/pubsub/TbPubSubNodeTest.java | 16 +- .../rule/engine/kafka/TbKafkaNodeTest.java | 8 +- .../metadata/TbGetTelemetryNodeTest.java | 8 +- .../rule/engine/mqtt/TbMqttNodeTest.java | 4 +- .../engine/rabbitmq/TbRabbitMqNodeTest.java | 4 +- .../transform/TbChangeOriginatorNodeTest.java | 16 +- 34 files changed, 314 insertions(+), 215 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index 0c3c2b749d..4e8dce76a7 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -377,7 +377,12 @@ public class DefaultTbContext implements TbContext { @Override public TbMsg transformMsg(TbMsg origMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) { - return TbMsg.transformMsg(origMsg, type, originator, metaData, data); + return origMsg.transform() + .type(type) + .originator(originator) + .metaData(metaData) + .data(data) + .build(); } @Override @@ -401,17 +406,27 @@ public class DefaultTbContext implements TbContext { @Override public TbMsg transformMsg(TbMsg origMsg, TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data) { - return TbMsg.transformMsg(origMsg, type, originator, metaData, data); + return origMsg.transform() + .type(type) + .originator(originator) + .metaData(metaData) + .data(data) + .build(); } @Override public TbMsg transformMsg(TbMsg origMsg, TbMsgMetaData metaData, String data) { - return TbMsg.transformMsg(origMsg, metaData, data); + return origMsg.transform() + .metaData(metaData) + .data(data) + .build(); } @Override public TbMsg transformMsgOriginator(TbMsg origMsg, EntityId originator) { - return TbMsg.transformMsgOriginator(origMsg, originator); + return origMsg.transform() + .originator(originator) + .build(); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java index 301f8e8838..92ad0e446a 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java @@ -290,11 +290,16 @@ public class DefaultTbClusterService implements TbClusterService { boolean isQueueTransform = targetQueueName != null && !targetQueueName.equals(tbMsg.getQueueName()); if (isRuleChainTransform && isQueueTransform) { - tbMsg = TbMsg.transformMsg(tbMsg, targetRuleChainId, targetQueueName); + tbMsg = tbMsg.transform() + .queueName(targetQueueName) + .ruleChainId(targetRuleChainId) + .build(); } else if (isRuleChainTransform) { - tbMsg = TbMsg.transformMsgRuleChainId(tbMsg, targetRuleChainId); + tbMsg = tbMsg.transform() + .ruleChainId(targetRuleChainId) + .build(); } else if (isQueueTransform) { - tbMsg = TbMsg.transformMsgQueueName(tbMsg, targetQueueName); + tbMsg = tbMsg.transform(targetQueueName); } } return tbMsg; diff --git a/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java b/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java index a022d6a5b1..c756e50de4 100644 --- a/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java +++ b/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java @@ -147,6 +147,10 @@ public class RuleNodeJsScriptEngine extends RuleNodeScriptEngine ListenableFuture wrongResultType(Object result) { diff --git a/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbClusterServiceTest.java b/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbClusterServiceTest.java index eed0734e6c..81a1120d4e 100644 --- a/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbClusterServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbClusterServiceTest.java @@ -371,7 +371,7 @@ public class DefaultTbClusterServiceTest { clusterService.pushMsgToRuleEngine(tenantId, deviceId, requestMsg, false, callback); verify(producerProvider).getRuleEngineMsgProducer(); - TbMsg expectedMsg = TbMsg.transformMsgQueueName(requestMsg, DataConstants.MAIN_QUEUE_NAME); + TbMsg expectedMsg = requestMsg.transform(DataConstants.MAIN_QUEUE_NAME); ArgumentCaptor actualMsg = ArgumentCaptor.forClass(TbMsg.class); verify(ruleEngineProducerService).sendToRuleEngine(eq(tbREQueueProducer), eq(tenantId), actualMsg.capture(), eq(callback)); assertThat(actualMsg.getValue()).usingRecursiveComparison().ignoringFields("ctx").isEqualTo(expectedMsg); diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java index 66c48077c2..f758aa8e8a 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java @@ -81,82 +81,42 @@ public final class TbMsg implements Serializable { return ctx.getAndIncrementRuleNodeCounter(); } - /** - * Transforms an existing TbMsg instance by changing its message type, originator, metadata, and data. - * - *

Deprecated: This method is deprecated since version 3.6.0 and should only be used when you need to - * specify a custom message type that doesn't exist in the {@link TbMsgType} enum. For standard message types, - * it is recommended to use the {@link #transformMsg(TbMsg, TbMsgType, EntityId, TbMsgMetaData, String)} - * method instead.

- * - * - * @param tbMsg the TbMsg instance to transform - * @param type the new message type - * @param originator the new originator - * @param metaData the new metadata - * @param data the new data - * @return the transformed TbMsg instance - */ - @Deprecated(since = "3.6.0") - public static TbMsg transformMsg(TbMsg tbMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) { - return tbMsg.transform() - .type(type) - .originator(originator) - .metaData(metaData) - .data(data) - .ctx(tbMsg.ctx) + public TbMsg transform(String queueName) { + return transform() + .queueName(queueName) + .ruleNodeId(null) .build(); } - public static TbMsg transformMsg(TbMsg tbMsg, TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, type, type.name(), originator, tbMsg.customerId, metaData.copy(), tbMsg.dataType, - data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.callback); - } - - public static TbMsg transformMsgOriginator(TbMsg tbMsg, EntityId originatorId) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, originatorId, tbMsg.getCustomerId(), tbMsg.metaData, tbMsg.dataType, - tbMsg.data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); - } - - public static TbMsg transformMsgData(TbMsg tbMsg, String data) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, - data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); - } - - public static TbMsg transformMsgMetadata(TbMsg tbMsg, TbMsgMetaData metadata) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, metadata.copy(), tbMsg.dataType, - tbMsg.data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); - } - - public static TbMsg transformMsg(TbMsg tbMsg, TbMsgMetaData metadata, String data) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, metadata, tbMsg.dataType, - data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); - } - - public static TbMsg transformMsgCustomerId(TbMsg tbMsg, CustomerId customerId) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, customerId, tbMsg.metaData, tbMsg.dataType, - tbMsg.data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); + // used for enqueueForTellNext + public static TbMsg newMsg(TbMsg tbMsg, String queueName, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { + return tbMsg.transform() + .id(UUID.randomUUID()) + .queueName(queueName) + .metaData(tbMsg.getMetaData()) + .ruleChainId(ruleChainId) + .ruleNodeId(ruleNodeId) + .callback(TbMsgCallback.EMPTY) + .build(); } - public static TbMsg transformMsgRuleChainId(TbMsg tbMsg, RuleChainId ruleChainId) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, - tbMsg.data, ruleChainId, null, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); + public TbMsg copyWithRuleChainId(RuleChainId ruleChainId) { + return copyWithRuleChainId(ruleChainId, this.id); } - public static TbMsg transformMsgQueueName(TbMsg tbMsg, String queueName) { - return new TbMsg(queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, - tbMsg.data, tbMsg.getRuleChainId(), null, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); + public TbMsg copyWithRuleChainId(RuleChainId ruleChainId, UUID msgId) { + return new TbMsg(this.queueName, msgId, this.ts, this.internalType, this.type, this.originator, this.customerId, + this.metaData, this.dataType, this.data, ruleChainId, null, this.correlationId, this.partition, this.ctx, callback); } - public static TbMsg transformMsg(TbMsg tbMsg, RuleChainId ruleChainId, String queueName) { - return new TbMsg(queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, - tbMsg.data, ruleChainId, null, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), tbMsg.getCallback()); + public TbMsg copyWithRuleNodeId(RuleChainId ruleChainId, RuleNodeId ruleNodeId, UUID msgId) { + return new TbMsg(this.queueName, msgId, this.ts, this.internalType, this.type, this.originator, this.customerId, + this.metaData, this.dataType, this.data, ruleChainId, ruleNodeId, this.correlationId, this.partition, this.ctx, callback); } - //used for enqueueForTellNext - public static TbMsg newMsg(TbMsg tbMsg, String queueName, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return new TbMsg(queueName, UUID.randomUUID(), tbMsg.getTs(), tbMsg.getInternalType(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.customerId, tbMsg.getMetaData().copy(), - tbMsg.getDataType(), tbMsg.getData(), ruleChainId, ruleNodeId, tbMsg.correlationId, tbMsg.partition, tbMsg.ctx.copy(), TbMsgCallback.EMPTY); + public TbMsg copyWithNewCtx() { + return new TbMsg(this.queueName, this.id, this.ts, this.internalType, this.type, this.originator, this.customerId, + this.metaData, this.dataType, this.data, ruleChainId, ruleNodeId, this.correlationId, this.partition, this.ctx.copy(), TbMsgCallback.EMPTY); } private TbMsg(String queueName, UUID id, long ts, TbMsgType internalType, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data, @@ -278,25 +238,6 @@ public final class TbMsg implements Serializable { } } - public TbMsg copyWithRuleChainId(RuleChainId ruleChainId) { - return copyWithRuleChainId(ruleChainId, this.id); - } - - public TbMsg copyWithRuleChainId(RuleChainId ruleChainId, UUID msgId) { - return new TbMsg(this.queueName, msgId, this.ts, this.internalType, this.type, this.originator, this.customerId, - this.metaData, this.dataType, this.data, ruleChainId, null, this.correlationId, this.partition, this.ctx, callback); - } - - public TbMsg copyWithRuleNodeId(RuleChainId ruleChainId, RuleNodeId ruleNodeId, UUID msgId) { - return new TbMsg(this.queueName, msgId, this.ts, this.internalType, this.type, this.originator, this.customerId, - this.metaData, this.dataType, this.data, ruleChainId, ruleNodeId, this.correlationId, this.partition, this.ctx, callback); - } - - public TbMsg copyWithNewCtx() { - return new TbMsg(this.queueName, this.id, this.ts, this.internalType, this.type, this.originator, this.customerId, - this.metaData, this.dataType, this.data, ruleChainId, ruleNodeId, this.correlationId, this.partition, this.ctx.copy(), TbMsgCallback.EMPTY); - } - public TbMsgCallback getCallback() { // May be null in case of deserialization; return Objects.requireNonNullElse(callback, TbMsgCallback.EMPTY); @@ -357,62 +298,91 @@ public final class TbMsg implements Serializable { } public TbMsgBuilder transform() { - return new TbMsgTransformer() - .queueName(this.queueName) - .id(this.id) - .ts(this.ts) - .type(this.type) - .type(this.internalType) - .originator(this.originator) - .customerId(this.customerId) - .metaData(this.metaData) - .dataType(this.dataType) - .data(this.data) - .ruleChainId(this.ruleChainId) - .ruleNodeId(this.ruleNodeId) - .correlationId(this.correlationId) - .partition(this.partition) - .ctx(this.ctx) - .callback(this.callback); + return new TbMsgTransformer(this); + } + + public TbMsgBuilder copy() { + return new TbMsgBuilder(this); } private static class TbMsgTransformer extends TbMsgBuilder { + TbMsgTransformer(TbMsg tbMsg) { + super(tbMsg); + } + + /* + * metadata is only copied if specified explicitly during transform + * */ @Override public TbMsgTransformer metaData(TbMsgMetaData metaData) { - super.metaData(metaData.copy()); + this.metaData = metaData.copy(); return this; } + /* + * setting ruleNodeId to null when updating ruleChainId + * */ @Override - public TbMsgTransformer ctx(TbMsgProcessingCtx ctx) { - super.ctx(ctx.copy()); + public TbMsgBuilder ruleChainId(RuleChainId ruleChainId) { + this.ruleChainId = ruleChainId; + this.ruleNodeId = null; return this; } + @Override + public TbMsg build() { + /* + * always copying ctx when transforming + * */ + if (ctx != null) { + ctx = ctx.copy(); + } + return super.build(); + } + } - private static class TbMsgBuilder { - - private String queueName; - private UUID id; - private long ts; - private String type; - private TbMsgType internalType; - private EntityId originator; - private CustomerId customerId; - private TbMsgMetaData metaData; - private TbMsgDataType dataType; - private String data; - private RuleChainId ruleChainId; - private RuleNodeId ruleNodeId; - private UUID correlationId; - private Integer partition; - private TbMsgProcessingCtx ctx; - private TbMsgCallback callback; + public static class TbMsgBuilder { + + protected String queueName; + protected UUID id; + protected long ts; + protected String type; + protected TbMsgType internalType; + protected EntityId originator; + protected CustomerId customerId; + protected TbMsgMetaData metaData; + protected TbMsgDataType dataType; + protected String data; + protected RuleChainId ruleChainId; + protected RuleNodeId ruleNodeId; + protected UUID correlationId; + protected Integer partition; + protected TbMsgProcessingCtx ctx; + protected TbMsgCallback callback; TbMsgBuilder() {} + TbMsgBuilder(TbMsg tbMsg) { + this.queueName = tbMsg.queueName; + this.id = tbMsg.id; + this.ts = tbMsg.ts; + this.type = tbMsg.type; + this.internalType = tbMsg.internalType; + this.originator = tbMsg.originator; + this.customerId = tbMsg.customerId; + this.metaData = tbMsg.metaData; + this.dataType = tbMsg.dataType; + this.data = tbMsg.data; + this.ruleChainId = tbMsg.ruleChainId; + this.ruleNodeId = tbMsg.ruleNodeId; + this.correlationId = tbMsg.correlationId; + this.partition = tbMsg.partition; + this.ctx = tbMsg.ctx; + this.callback = tbMsg.callback; + } + public TbMsgBuilder queueName(String queueName) { this.queueName = queueName; return this; @@ -442,6 +412,7 @@ public final class TbMsg implements Serializable { public TbMsgBuilder type(TbMsgType internalType) { this.internalType = internalType; + this.type = internalType.name(); return this; } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractAlarmNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractAlarmNode.java index 855727494e..5431913894 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractAlarmNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractAlarmNode.java @@ -79,7 +79,9 @@ public abstract class TbAbstractAlarmNode args) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNode.java index 329fa46c3b..75d0e774a2 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNode.java @@ -175,7 +175,9 @@ public class CalculateDeltaNode implements TbNode { long period = previousData != null ? msg.getMetaDataTs() - previousData.ts : 0; json.put(config.getPeriodValueKey(), period); } - return TbMsg.transformMsgData(msg, JacksonUtil.toString(json)); + return msg.transform() + .data(JacksonUtil.toString(json)) + .build(); }, MoreExecutors.directExecutor()); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractNodeWithFetchTo.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractNodeWithFetchTo.java index 951f1635d8..0e4fc27c02 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractNodeWithFetchTo.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractNodeWithFetchTo.java @@ -82,9 +82,13 @@ public abstract class TbAbstractNodeWithFetchTo> list = ctx.getTimeseriesService().findAll(ctx.getTenantId(), msg.getOriginator(), buildQueries(interval, keys)); DonAsynchron.withCallback(list, data -> { var metaData = updateMetadata(data, msg, keys); - ctx.tellSuccess(TbMsg.transformMsgMetadata(msg, metaData)); + ctx.tellSuccess(msg.transform() + .metaData(metaData) + .build()); }, error -> ctx.tellFailure(msg, error), ctx.getDbCallbackExecutor()); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java index 912496f39e..56bfe038a8 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java @@ -103,7 +103,9 @@ public class TbMqttNode extends TbAbstractExternalNode { private TbMsg processException(TbMsg origMsg, Throwable e) { TbMsgMetaData metaData = origMsg.getMetaData().copy(); metaData.putValue(ERROR, e.getClass() + ": " + e.getMessage()); - return TbMsg.transformMsgMetadata(origMsg, metaData); + return origMsg.transform() + .metaData(metaData) + .build(); } @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java index 34c5d6de28..8ae3da7bad 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java @@ -82,7 +82,9 @@ public class TbNotificationNode extends TbAbstractExternalNode { public void onSuccess(NotificationRequestStats stats) { TbMsgMetaData metaData = tbMsg.getMetaData().copy(); metaData.putValue("notificationRequestResult", JacksonUtil.toString(stats)); - tellSuccess(ctx, TbMsg.transformMsgMetadata(tbMsg, metaData)); + tellSuccess(ctx, tbMsg.transform() + .metaData(metaData) + .build()); } @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNode.java index b15e0e8e3d..8b724ae86e 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNode.java @@ -127,7 +127,9 @@ public class TbRabbitMqNode extends TbAbstractExternalNode { private TbMsg processException(TbMsg origMsg, Throwable t) { TbMsgMetaData metaData = origMsg.getMetaData().copy(); metaData.putValue(ERROR, t.getClass() + ": " + t.getMessage()); - return TbMsg.transformMsgMetadata(origMsg, metaData); + return origMsg.transform() + .metaData(metaData) + .build(); } @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java index 566430bd8d..ed1ed4fa83 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java @@ -323,7 +323,9 @@ public class TbHttpClient { metaData.putValue(STATUS_REASON, httpStatus.getReasonPhrase()); metaData.putValue(ERROR_BODY, response.getBody()); headersToMetaData(response.getHeaders(), metaData::putValue); - return TbMsg.transformMsgMetadata(origMsg, metaData); + return origMsg.transform() + .metaData(metaData) + .build(); } private TbMsg processException(TbMsg origMsg, Throwable e) { @@ -334,7 +336,9 @@ public class TbHttpClient { metaData.putValue(STATUS_CODE, restClientResponseException.getStatusCode().value() + ""); metaData.putValue(ERROR_BODY, restClientResponseException.getResponseBodyAsString()); } - return TbMsg.transformMsgMetadata(origMsg, metaData); + return origMsg.transform() + .metaData(metaData) + .build(); } private void prepareHeaders(HttpHeaders headers, TbMsg msg) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNode.java index aedf8eac1e..1417618a54 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNode.java @@ -105,7 +105,10 @@ public class TbCopyKeysNode extends TbAbstractTransformNodeWithTbMsgSource { log.debug("Unexpected CopyFrom value: {}. Allowed values: {}", copyFrom, TbMsgSource.values()); } } - ctx.tellSuccess(msgChanged ? TbMsg.transformMsg(msg, metaDataCopy, msgData) : msg); + ctx.tellSuccess(msgChanged ? msg.transform() + .metaData(metaDataCopy) + .data(msgData) + .build() : msg); } @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNode.java index 3d4c9b9684..cf31381af2 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNode.java @@ -100,7 +100,10 @@ public class TbDeleteKeysNode extends TbAbstractTransformNodeWithTbMsgSource { default: log.debug("Unexpected DeleteFrom value: {}. Allowed values: {}", deleteFrom, TbMsgSource.values()); } - ctx.tellSuccess(hasNoChanges ? msg : TbMsg.transformMsg(msg, metaDataCopy, msgDataStr)); + ctx.tellSuccess(hasNoChanges ? msg : msg.transform() + .metaData(metaDataCopy) + .data(msgDataStr) + .build()); } @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbJsonPathNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbJsonPathNode.java index b0a7bc059f..043e914c2c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbJsonPathNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbJsonPathNode.java @@ -68,7 +68,9 @@ public class TbJsonPathNode implements TbNode { if (!TbJsonPathNodeConfiguration.DEFAULT_JSON_PATH.equals(this.jsonPathValue)) { try { Object jsonPathData = jsonPath.read(msg.getData(), this.configurationJsonPath); - ctx.tellSuccess(TbMsg.transformMsgData(msg, JacksonUtil.toString(jsonPathData))); + ctx.tellSuccess(msg.transform() + .data(JacksonUtil.toString(jsonPathData)) + .build()); } catch (PathNotFoundException e) { ctx.tellFailure(msg, e); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNode.java index f01f09ae49..490504c42b 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNode.java @@ -106,7 +106,10 @@ public class TbRenameKeysNode extends TbAbstractTransformNodeWithTbMsgSource { default: log.debug("Unexpected RenameIn value: {}. Allowed values: {}", renameIn, TbMsgSource.values()); } - ctx.tellSuccess(msgChanged ? TbMsg.transformMsg(msg, metaDataCopy, data) : msg); + ctx.tellSuccess(msgChanged ? msg.transform() + .metaData(metaDataCopy) + .data(data) + .build() : msg); } @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNode.java index fb12352dc0..92a3dd8d09 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNode.java @@ -64,7 +64,9 @@ public class TbSplitArrayMsgNode implements TbNode { if (data.isEmpty()) { ctx.ack(msg); } else if (data.size() == 1) { - ctx.tellSuccess(TbMsg.transformMsgData(msg, JacksonUtil.toString(data.get(0)))); + ctx.tellSuccess(msg.transform() + .data(JacksonUtil.toString(data.get(0))) + .build()); } else { TbMsgCallbackWrapper wrapper = new MultipleTbMsgsCallbackWrapper(data.size(), new TbMsgCallback() { @Override @@ -78,7 +80,9 @@ public class TbSplitArrayMsgNode implements TbNode { } }); data.forEach(msgNode -> { - TbMsg outMsg = TbMsg.transformMsgData(msg, JacksonUtil.toString(msgNode)); + TbMsg outMsg = msg.transform() + .data(JacksonUtil.toString(msgNode)) + .build(); ctx.enqueueForTellNext(outMsg, TbNodeConnectionType.SUCCESS, wrapper::onSuccess, wrapper::onFailure); }); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java index 7846ac2da5..5f35288c30 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java @@ -229,12 +229,12 @@ class TbCreateAlarmNodeTest { given(alarmServiceMock.createAlarm(expectedCreateAlarmRequest)).willReturn(apiCallResult); given(ctxMock.alarmActionMsg(expectedCreatedAlarmInfo, ruleNodeSelfId, TbMsgType.ENTITY_CREATED)).willReturn(alarmActionMsgMock); given(ctxMock.transformMsg(any(TbMsg.class), any(TbMsgType.class), any(EntityId.class), any(TbMsgMetaData.class), anyString())) - .willAnswer(answer -> TbMsg.transformMsg( - answer.getArgument(0, TbMsg.class), - answer.getArgument(1, TbMsgType.class), - answer.getArgument(2, EntityId.class), - answer.getArgument(3, TbMsgMetaData.class), - answer.getArgument(4, String.class)) + .willAnswer(answer -> answer.getArgument(0, TbMsg.class).transform() + .type(answer.getArgument(1, TbMsgType.class)) + .originator(answer.getArgument(2, EntityId.class)) + .metaData(answer.getArgument(3, TbMsgMetaData.class)) + .data(answer.getArgument(4, String.class)) + .build() ); given(ctxMock.createScriptEngine(ScriptLanguage.TBEL, TbAbstractAlarmNodeConfiguration.ALARM_DETAILS_BUILD_TBEL_TEMPLATE)).willReturn(alarmDetailsScriptMock); @@ -403,12 +403,12 @@ class TbCreateAlarmNodeTest { given(alarmServiceMock.createAlarm(expectedCreateAlarmRequest)).willReturn(apiCallResult); given(ctxMock.alarmActionMsg(expectedCreatedAlarmInfo, ruleNodeSelfId, TbMsgType.ENTITY_CREATED)).willReturn(alarmActionMsgMock); given(ctxMock.transformMsg(any(TbMsg.class), any(TbMsgType.class), any(EntityId.class), any(TbMsgMetaData.class), anyString())) - .willAnswer(answer -> TbMsg.transformMsg( - answer.getArgument(0, TbMsg.class), - answer.getArgument(1, TbMsgType.class), - answer.getArgument(2, EntityId.class), - answer.getArgument(3, TbMsgMetaData.class), - answer.getArgument(4, String.class)) + .willAnswer(answer -> answer.getArgument(0, TbMsg.class).transform() + .type(answer.getArgument(1, TbMsgType.class)) + .originator(answer.getArgument(2, EntityId.class)) + .metaData(answer.getArgument(3, TbMsgMetaData.class)) + .data(answer.getArgument(4, String.class)) + .build() ); given(ctxMock.createScriptEngine(ScriptLanguage.JS, config.getAlarmDetailsBuildJs())).willReturn(alarmDetailsScriptMock); @@ -598,12 +598,12 @@ class TbCreateAlarmNodeTest { given(alarmServiceMock.updateAlarm(expectedUpdateAlarmRequest)).willReturn(apiCallResult); given(ctxMock.alarmActionMsg(expectedUpdatedAlarmInfo, ruleNodeSelfId, TbMsgType.ENTITY_UPDATED)).willReturn(alarmActionMsgMock); given(ctxMock.transformMsg(any(TbMsg.class), any(TbMsgType.class), any(EntityId.class), any(TbMsgMetaData.class), anyString())) - .willAnswer(answer -> TbMsg.transformMsg( - answer.getArgument(0, TbMsg.class), - answer.getArgument(1, TbMsgType.class), - answer.getArgument(2, EntityId.class), - answer.getArgument(3, TbMsgMetaData.class), - answer.getArgument(4, String.class)) + .willAnswer(answer -> answer.getArgument(0, TbMsg.class).transform() + .type(answer.getArgument(1, TbMsgType.class)) + .originator(answer.getArgument(2, EntityId.class)) + .metaData(answer.getArgument(3, TbMsgMetaData.class)) + .data(answer.getArgument(4, String.class)) + .build() ); given(ctxMock.createScriptEngine(ScriptLanguage.TBEL, config.getAlarmDetailsBuildTbel())).willReturn(alarmDetailsScriptMock); @@ -775,12 +775,12 @@ class TbCreateAlarmNodeTest { given(alarmServiceMock.createAlarm(expectedCreateAlarmRequest)).willReturn(apiCallResult); given(ctxMock.alarmActionMsg(expectedCreatedAlarmInfo, ruleNodeSelfId, TbMsgType.ENTITY_CREATED)).willReturn(alarmActionMsgMock); given(ctxMock.transformMsg(any(TbMsg.class), any(TbMsgType.class), any(EntityId.class), any(TbMsgMetaData.class), anyString())) - .willAnswer(answer -> TbMsg.transformMsg( - answer.getArgument(0, TbMsg.class), - answer.getArgument(1, TbMsgType.class), - answer.getArgument(2, EntityId.class), - answer.getArgument(3, TbMsgMetaData.class), - answer.getArgument(4, String.class)) + .willAnswer(answer -> answer.getArgument(0, TbMsg.class).transform() + .type(answer.getArgument(1, TbMsgType.class)) + .originator(answer.getArgument(2, EntityId.class)) + .metaData(answer.getArgument(3, TbMsgMetaData.class)) + .data(answer.getArgument(4, String.class)) + .build() ); given(ctxMock.createScriptEngine(ScriptLanguage.TBEL, config.getAlarmDetailsBuildTbel())).willReturn(alarmDetailsScriptMock); @@ -967,12 +967,12 @@ class TbCreateAlarmNodeTest { given(alarmServiceMock.updateAlarm(expectedUpdateAlarmRequest)).willReturn(apiCallResult); given(ctxMock.alarmActionMsg(expectedUpdatedAlarmInfo, ruleNodeSelfId, TbMsgType.ENTITY_UPDATED)).willReturn(alarmActionMsgMock); given(ctxMock.transformMsg(any(TbMsg.class), any(TbMsgType.class), any(EntityId.class), any(TbMsgMetaData.class), anyString())) - .willAnswer(answer -> TbMsg.transformMsg( - answer.getArgument(0, TbMsg.class), - answer.getArgument(1, TbMsgType.class), - answer.getArgument(2, EntityId.class), - answer.getArgument(3, TbMsgMetaData.class), - answer.getArgument(4, String.class)) + .willAnswer(answer -> answer.getArgument(0, TbMsg.class).transform() + .type(answer.getArgument(1, TbMsgType.class)) + .originator(answer.getArgument(2, EntityId.class)) + .metaData(answer.getArgument(3, TbMsgMetaData.class)) + .data(answer.getArgument(4, String.class)) + .build() ); given(ctxMock.createScriptEngine(ScriptLanguage.TBEL, config.getAlarmDetailsBuildTbel())).willReturn(alarmDetailsScriptMock); @@ -1153,12 +1153,12 @@ class TbCreateAlarmNodeTest { given(alarmServiceMock.updateAlarm(expectedUpdateAlarmRequest)).willReturn(apiCallResult); given(ctxMock.alarmActionMsg(expectedUpdatedAlarmInfo, ruleNodeSelfId, TbMsgType.ENTITY_UPDATED)).willReturn(alarmActionMsgMock); given(ctxMock.transformMsg(any(TbMsg.class), any(TbMsgType.class), any(EntityId.class), any(TbMsgMetaData.class), anyString())) - .willAnswer(answer -> TbMsg.transformMsg( - answer.getArgument(0, TbMsg.class), - answer.getArgument(1, TbMsgType.class), - answer.getArgument(2, EntityId.class), - answer.getArgument(3, TbMsgMetaData.class), - answer.getArgument(4, String.class)) + .willAnswer(answer -> answer.getArgument(0, TbMsg.class).transform() + .type(answer.getArgument(1, TbMsgType.class)) + .originator(answer.getArgument(2, EntityId.class)) + .metaData(answer.getArgument(3, TbMsgMetaData.class)) + .data(answer.getArgument(4, String.class)) + .build() ); given(ctxMock.createScriptEngine(ScriptLanguage.TBEL, config.getAlarmDetailsBuildTbel())).willReturn(alarmDetailsScriptMock); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateRelationNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateRelationNodeTest.java index 6e707a4415..37d7ee7872 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateRelationNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateRelationNodeTest.java @@ -439,7 +439,9 @@ public class TbCreateRelationNodeTest extends AbstractRuleNodeUpgradeTest { var md = getMetadataWithNameTemplate(); var msg = getTbMsg(originatorId, md); - var msgAfterOriginatorChanged = TbMsg.transformMsgOriginator(msg, originatorId); + var msgAfterOriginatorChanged = msg.transform() + .originator(originatorId) + .build(); when(ctxMock.transformMsgOriginator(any(), any())).thenReturn(msgAfterOriginatorChanged); // WHEN diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNodeTest.java index e0e3d6d0d4..b05fc3520c 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNodeTest.java @@ -176,7 +176,10 @@ public class TbAwsLambdaNodeTest { assertThat(invokeRequestCaptor.getValue().getQualifier()).isEqualTo(expectedQualifier); TbMsgMetaData resultMsgMetadata = metadata.copy(); resultMsgMetadata.putValue("requestId", requestIdStr); - TbMsg resultedMsg = TbMsg.transformMsg(msg, resultMsgMetadata, funcResponsePayload); + TbMsg resultedMsg = msg.transform() + .metaData(resultMsgMetadata) + .data(funcResponsePayload) + .build(); assertThat(msgCaptor.getValue()).usingRecursiveComparison() .ignoringFields("ctx") .isEqualTo(resultedMsg); @@ -231,7 +234,9 @@ public class TbAwsLambdaNodeTest { verify(ctx).tellFailure(msgCaptor.capture(), throwableCaptor.capture()); var metadata = Map.of("error", RuntimeException.class + ": " + errorMsg, "requestId", requestIdStr); - TbMsg resultedMsg = TbMsg.transformMsgMetadata(msg, new TbMsgMetaData(metadata)); + TbMsg resultedMsg = msg.transform() + .metaData(new TbMsgMetaData(metadata)) + .build(); assertThat(msgCaptor.getValue()).usingRecursiveComparison() .ignoringFields("ctx") @@ -270,7 +275,10 @@ public class TbAwsLambdaNodeTest { verify(ctx).tellSuccess(msgCaptor.capture()); Map metadata = Map.of("requestId", requestIdStr); - TbMsg resultedMsg = TbMsg.transformMsg(msg, new TbMsgMetaData(metadata), payload); + TbMsg resultedMsg = msg.transform() + .metaData(new TbMsgMetaData(metadata)) + .data(payload) + .build(); assertThat(msgCaptor.getValue()).usingRecursiveComparison() .ignoringFields("ctx") @@ -309,7 +317,9 @@ public class TbAwsLambdaNodeTest { verify(ctx).tellFailure(msgCaptor.capture(), throwableCaptor.capture()); var metadata = Map.of("error", RuntimeException.class + ": " + errorMsg, "requestId", requestIdStr); - TbMsg resultedMsg = TbMsg.transformMsgMetadata(msg, new TbMsgMetaData(metadata)); + TbMsg resultedMsg = msg.transform() + .metaData(new TbMsgMetaData(metadata)) + .build(); assertThat(msgCaptor.getValue()).usingRecursiveComparison() .ignoringFields("ctx") diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNodeTest.java index d7ae0be329..dcf087af13 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNodeTest.java @@ -138,7 +138,9 @@ class TbPubSubNodeTest { ArgumentCaptor actualMsg = ArgumentCaptor.forClass(TbMsg.class); then(ctxMock).should().enqueueForTellNext(actualMsg.capture(), eq(TbNodeConnectionType.SUCCESS)); metaData.putValue("messageId", messageId); - TbMsg expectedMsg = TbMsg.transformMsgMetadata(msg, metaData); + TbMsg expectedMsg = msg.transform() + .metaData(metaData) + .build(); assertThat(actualMsg.getValue()) .usingRecursiveComparison() .ignoringFields("ctx") @@ -184,7 +186,9 @@ class TbPubSubNodeTest { ArgumentCaptor actualMsg = ArgumentCaptor.forClass(TbMsg.class); then(ctxMock).should().tellSuccess(actualMsg.capture()); metadata.putValue("messageId", messageId); - TbMsg expectedMsg = TbMsg.transformMsgMetadata(msg, metadata); + TbMsg expectedMsg = msg.transform() + .metaData(metadata) + .build(); assertThat(actualMsg.getValue()) .usingRecursiveComparison() .ignoringFields("ctx") @@ -216,7 +220,9 @@ class TbPubSubNodeTest { ArgumentCaptor actualError = ArgumentCaptor.forClass(Throwable.class); then(ctxMock).should().tellFailure(actualMsg.capture(), actualError.capture()); metaData.putValue("error", RuntimeException.class + ": " + errorMsg); - TbMsg expectedMsg = TbMsg.transformMsgMetadata(msg, metaData); + TbMsg expectedMsg = msg.transform() + .metaData(metaData) + .build(); assertThat(actualMsg.getValue()) .usingRecursiveComparison() .ignoringFields("ctx") @@ -249,7 +255,9 @@ class TbPubSubNodeTest { ArgumentCaptor actualError = ArgumentCaptor.forClass(Throwable.class); then(ctxMock).should().enqueueForTellFailure(actualMsg.capture(), actualError.capture()); metaData.putValue("error", RuntimeException.class + ": " + errorMsg); - TbMsg expectedMsg = TbMsg.transformMsgMetadata(msg, metaData); + TbMsg expectedMsg = msg.transform() + .metaData(metaData) + .build(); assertThat(actualMsg.getValue()) .usingRecursiveComparison() .ignoringFields("ctx") diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeTest.java index 71e896b4f4..4713f0cfd9 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeTest.java @@ -436,7 +436,9 @@ public class TbKafkaNodeTest { metaData.putValue("offset", String.valueOf(OFFSET)); metaData.putValue("partition", String.valueOf(PARTITION)); metaData.putValue("topic", expectedTopic); - TbMsg expectedMsg = TbMsg.transformMsgMetadata(originalMsg, metaData); + TbMsg expectedMsg = originalMsg.transform() + .metaData(metaData) + .build(); assertThat(actualMsg) .usingRecursiveComparison() .ignoringFields("ctx") @@ -446,7 +448,9 @@ public class TbKafkaNodeTest { private void verifyOutgoingFailureMsg(String errorMsg, TbMsg actualMsg, TbMsg originalMsg) { TbMsgMetaData metaData = originalMsg.getMetaData(); metaData.putValue("error", RuntimeException.class + ": " + errorMsg); - TbMsg expectedMsg = TbMsg.transformMsgMetadata(originalMsg, metaData); + TbMsg expectedMsg = originalMsg.transform() + .metaData(metaData) + .build(); assertThat(actualMsg).usingRecursiveComparison().ignoringFields("ctx").isEqualTo(expectedMsg); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNodeTest.java index 63044b311b..69d1200746 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNodeTest.java @@ -465,7 +465,9 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("temperature", "[{\"ts\":" + (ts - 5) + ",\"value\":23.1},{\"ts\":" + (ts - 4) + ",\"value\":22.4}]"); metaData.putValue("humidity", "[{\"ts\":" + (ts - 4) + ",\"value\":55.5}]"); - TbMsg expectedMsg = TbMsg.transformMsgMetadata(msg, metaData); + TbMsg expectedMsg = msg.transform() + .metaData(metaData) + .build(); assertThat(actualMsg.getValue()).usingRecursiveComparison().ignoringFields("ctx").isEqualTo(expectedMsg); } @@ -501,7 +503,9 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("temperature", "\"22.4\""); metaData.putValue("humidity", "\"55.5\""); - TbMsg expectedMsg = TbMsg.transformMsgMetadata(msg, metaData); + TbMsg expectedMsg = msg.transform() + .metaData(metaData) + .build(); assertThat(actualMsg.getValue()).usingRecursiveComparison().ignoringFields("ctx").isEqualTo(expectedMsg); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeTest.java index 6b074da45a..aa6700dd20 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeTest.java @@ -340,7 +340,9 @@ public class TbMqttNodeTest extends AbstractRuleNodeUpgradeTest { then(mqttClientMock).should().publish(mqttNodeConfig.getTopicPattern(), Unpooled.wrappedBuffer(expectedData.getBytes(StandardCharsets.UTF_8)), MqttQoS.AT_LEAST_ONCE, false); TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("error", RuntimeException.class + ": " + errorMsg); - TbMsg expectedMsg = TbMsg.transformMsgMetadata(msg, metaData); + TbMsg expectedMsg = msg.transform() + .metaData(metaData) + .build(); ArgumentCaptor actualMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); then(ctxMock).should().tellFailure(actualMsgCaptor.capture(), eq(exception)); TbMsg actualMsg = actualMsgCaptor.getValue(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java index 24d3a51cd8..914605a8d4 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java @@ -227,7 +227,9 @@ public class TbRabbitMqNodeTest { () -> then(ctxMock).should().tellFailure(actualMsg.capture(), throwable.capture()); verifyTellFailure.run(); metaData.putValue("error", RuntimeException.class + ": " + errorMsg); - TbMsg expectedMsg = TbMsg.transformMsgMetadata(msg, metaData); + TbMsg expectedMsg = msg.transform() + .metaData(metaData) + .build(); assertThat(actualMsg.getValue()).usingRecursiveComparison().ignoringFields("ctx").isEqualTo(expectedMsg); assertThat(throwable.getValue()).isInstanceOf(RuntimeException.class).hasMessage(errorMsg); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java index 455394e13f..851179bc04 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java @@ -182,7 +182,9 @@ public class TbChangeOriginatorNodeTest { .metaData(TbMsgMetaData.EMPTY.copy()) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); - TbMsg expectedMsg = TbMsg.transformMsgOriginator(msg, CUSTOMER_ID); + TbMsg expectedMsg = msg.transform() + .originator(CUSTOMER_ID) + .build(); given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); given(ctxMock.getDeviceService()).willReturn(deviceServiceMock); @@ -210,7 +212,9 @@ public class TbChangeOriginatorNodeTest { .metaData(TbMsgMetaData.EMPTY.copy()) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); - TbMsg expectedMsg = TbMsg.transformMsgOriginator(msg, TENANT_ID); + TbMsg expectedMsg = msg.transform() + .originator(TENANT_ID) + .build(); given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); given(ctxMock.getTenantId()).willReturn(TENANT_ID); @@ -274,7 +278,9 @@ public class TbChangeOriginatorNodeTest { .metaData(TbMsgMetaData.EMPTY.copy()) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); - TbMsg expectedMsg = TbMsg.transformMsgOriginator(msg, DEVICE_ID); + TbMsg expectedMsg = msg.transform() + .originator(DEVICE_ID) + .build(); given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); given(ctxMock.getAlarmService()).willReturn(alarmServiceMock); @@ -305,7 +311,9 @@ public class TbChangeOriginatorNodeTest { .metaData(metaData.copy()) .data(data) .build(); - TbMsg expectedMsg = TbMsg.transformMsgOriginator(msg, ASSET_ID); + TbMsg expectedMsg = msg.transform() + .originator(ASSET_ID) + .build(); given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); given(ctxMock.getAssetService()).willReturn(assetServiceMock); From 1b6256a9e82b8741dda7fdecd93ea1468833f916 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 13 Dec 2024 15:18:37 +0200 Subject: [PATCH 010/108] Refactor TbMsg copying --- .../actors/ruleChain/DefaultTbContext.java | 5 +- .../RuleChainActorMessageProcessor.java | 19 ++++-- .../thingsboard/server/common/msg/TbMsg.java | 62 +++++++------------ 3 files changed, 42 insertions(+), 44 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index 4e8dce76a7..eed7093cd0 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -173,7 +173,10 @@ public class DefaultTbContext implements TbContext { if (!msg.isValid()) { return; } - TbMsg tbMsg = msg.copyWithRuleChainId(ruleChainId); + TbMsg tbMsg = msg.copy() + .ruleChainId(ruleChainId) + .ruleNodeId(null) + .build(); tbMsg.pushToStack(nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId()); TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getQueueName(), getTenantId(), tbMsg.getOriginator()); doEnqueue(tpi, tbMsg, new SimpleTbQueueCallback(md -> ack(msg), t -> tellFailure(msg, t))); diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java index 460da228c3..a2a0ab8c80 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java @@ -16,6 +16,7 @@ package org.thingsboard.server.actors.ruleChain; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.common.util.DebugModeUtil; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.TbActorCtx; import org.thingsboard.server.actors.TbActorRef; @@ -35,7 +36,6 @@ import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleNode; -import org.thingsboard.common.util.DebugModeUtil; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; import org.thingsboard.server.common.msg.plugin.RuleNodeUpdatedMsg; @@ -217,7 +217,10 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor Date: Mon, 16 Dec 2024 16:40:06 +0200 Subject: [PATCH 011/108] Refactor saveAndNotify for timeseries --- .../controller/TelemetryController.java | 57 ++++++---- .../service/edge/rpc/EdgeGrpcService.java | 23 ++-- .../ota/DefaultOtaPackageStateService.java | 62 +++++++---- .../csv/AbstractBulkImportService.java | 37 ++++--- .../DefaultTelemetrySubscriptionService.java | 103 +++--------------- .../server/controller/WebsocketApiTest.java | 36 +++--- .../api/RuleEngineTelemetryService.java | 40 +------ .../engine/api/TimeseriesSaveRequest.java | 102 +++++++++++++++++ .../rule/engine/math/TbMathNode.java | 12 +- .../engine/telemetry/TbMsgTimeseriesNode.java | 15 ++- .../rule/engine/math/TbMathNodeTest.java | 18 +-- .../telemetry/TbMsgTimeseriesNodeTest.java | 53 +++++---- 12 files changed, 317 insertions(+), 241 deletions(-) create mode 100644 rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java diff --git a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java index b1733a9af0..8c63be6de9 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java @@ -47,6 +47,7 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.request.async.DeferredResult; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.EntityType; @@ -399,7 +400,7 @@ public class TelemetryController extends BaseController { public DeferredResult saveEntityAttributesV2( @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, - @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE"}, requiredMode = Schema.RequiredMode.REQUIRED)) @PathVariable("scope")AttributeScope scope, + @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE"}, requiredMode = Schema.RequiredMode.REQUIRED)) @PathVariable("scope") AttributeScope scope, @io.swagger.v3.oas.annotations.parameters.RequestBody(description = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException { EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); return saveAttributes(getTenantId(), entityId, scope, request); @@ -423,8 +424,8 @@ public class TelemetryController extends BaseController { public DeferredResult saveEntityTelemetry( @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, - @Parameter(description = TELEMETRY_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = "ANY")) @PathVariable("scope")String scope, - @io.swagger.v3.oas.annotations.parameters.RequestBody(description = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody String requestBody) throws ThingsboardException { + @Parameter(description = TELEMETRY_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = "ANY")) @PathVariable("scope") String scope, + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody String requestBody) throws ThingsboardException { EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); return saveTelemetry(getTenantId(), entityId, requestBody, 0L); } @@ -447,9 +448,9 @@ public class TelemetryController extends BaseController { public DeferredResult saveEntityTelemetryWithTTL( @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, - @Parameter(description = TELEMETRY_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = "ANY")) @PathVariable("scope")String scope, - @Parameter(description = "A long value representing TTL (Time to Live) parameter.", required = true)@PathVariable("ttl")Long ttl, - @io.swagger.v3.oas.annotations.parameters.RequestBody(description = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody String requestBody) throws ThingsboardException { + @Parameter(description = TELEMETRY_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = "ANY")) @PathVariable("scope") String scope, + @Parameter(description = "A long value representing TTL (Time to Live) parameter.", required = true) @PathVariable("ttl") Long ttl, + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody String requestBody) throws ThingsboardException { EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); return saveTelemetry(getTenantId(), entityId, requestBody, ttl); } @@ -550,8 +551,8 @@ public class TelemetryController extends BaseController { @ResponseBody public DeferredResult deleteDeviceAttributes( @Parameter(description = DEVICE_ID_PARAM_DESCRIPTION, required = true) @PathVariable(DEVICE_ID) String deviceIdStr, - @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"}, requiredMode = Schema.RequiredMode.REQUIRED)) @PathVariable("scope")AttributeScope scope, - @Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION, required = true)@RequestParam(name = "keys")String keysStr) throws ThingsboardException { + @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"}, requiredMode = Schema.RequiredMode.REQUIRED)) @PathVariable("scope") AttributeScope scope, + @Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION, required = true) @RequestParam(name = "keys") String keysStr) throws ThingsboardException { EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); return deleteAttributes(entityId, scope, keysStr); } @@ -573,8 +574,8 @@ public class TelemetryController extends BaseController { public DeferredResult deleteEntityAttributes( @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, - @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"})) @PathVariable("scope")AttributeScope scope, - @Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION, required = true)@RequestParam(name = "keys")String keysStr) throws ThingsboardException { + @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"})) @PathVariable("scope") AttributeScope scope, + @Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION, required = true) @RequestParam(name = "keys") String keysStr) throws ThingsboardException { EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); return deleteAttributes(entityId, scope, keysStr); } @@ -587,7 +588,7 @@ public class TelemetryController extends BaseController { SecurityUser user = getCurrentUser(); return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_ATTRIBUTES, entityIdSrc, (result, tenantId, entityId) -> { - tsSubService.deleteAndNotify(tenantId, entityId, scope.name(), keys, new FutureCallback() { + tsSubService.deleteAndNotify(tenantId, entityId, scope, keys, new FutureCallback() { @Override public void onSuccess(@Nullable Void tmp) { logAttributesDeleted(user, entityId, scope, keys, null); @@ -672,19 +673,27 @@ public class TelemetryController extends BaseController { TenantProfile tenantProfile = tenantProfileCache.get(tenantId); tenantTtl = TimeUnit.DAYS.toSeconds(((DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration()).getDefaultStorageTtlDays()); } - tsSubService.saveAndNotify(tenantId, user.getCustomerId(), entityId, entries, tenantTtl, new FutureCallback() { - @Override - public void onSuccess(@Nullable Void tmp) { - logTelemetryUpdated(user, entityId, entries, null); - result.setResult(new ResponseEntity(HttpStatus.OK)); - } - - @Override - public void onFailure(Throwable t) { - logTelemetryUpdated(user, entityId, entries, t); - AccessValidator.handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR); - } - }); + tsSubService.save(TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .customerId(user.getCustomerId()) + .entityId(entityId) + .entries(entries) + .ttl(tenantTtl) + .saveLatest(true) + .callback(new FutureCallback() { + @Override + public void onSuccess(@Nullable Void tmp) { + logTelemetryUpdated(user, entityId, entries, null); + result.setResult(new ResponseEntity(HttpStatus.OK)); + } + + @Override + public void onFailure(Throwable t) { + logTelemetryUpdated(user, entityId, entries, t); + AccessValidator.handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR); + } + }) + .build()); }); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java index 71387aa542..d451bb36b3 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java @@ -32,6 +32,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.cache.TbTransactionalCache; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.AttributeScope; @@ -503,10 +504,13 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i private void save(TenantId tenantId, EdgeId edgeId, String key, long value) { log.debug("[{}][{}] Updating long edge telemetry [{}] [{}]", tenantId, edgeId, key, value); if (persistToTelemetry) { - tsSubService.saveAndNotify( - tenantId, edgeId, - Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry(key, value))), - new AttributeSaveCallback(tenantId, edgeId, key, value)); + tsSubService.save(TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .entityId(edgeId) + .entries(Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry(key, value)))) + .saveLatest(true) + .callback(new AttributeSaveCallback(tenantId, edgeId, key, value)) + .build()); } else { tsSubService.saveAttrAndNotify(tenantId, edgeId, AttributeScope.SERVER_SCOPE, key, value, new AttributeSaveCallback(tenantId, edgeId, key, value)); } @@ -515,10 +519,13 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i private void save(TenantId tenantId, EdgeId edgeId, String key, boolean value) { log.debug("[{}][{}] Updating boolean edge telemetry [{}] [{}]", tenantId, edgeId, key, value); if (persistToTelemetry) { - tsSubService.saveAndNotify( - tenantId, edgeId, - Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new BooleanDataEntry(key, value))), - new AttributeSaveCallback(tenantId, edgeId, key, value)); + tsSubService.save(TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .entityId(edgeId) + .entries(Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new BooleanDataEntry(key, value)))) + .saveLatest(true) + .callback(new AttributeSaveCallback(tenantId, edgeId, key, value)) + .build()); } else { tsSubService.saveAttrAndNotify(tenantId, edgeId, AttributeScope.SERVER_SCOPE, key, value, new AttributeSaveCallback(tenantId, edgeId, key, value)); } diff --git a/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java b/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java index 54ca38ad78..78da4eee7f 100644 --- a/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java @@ -21,6 +21,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.DataConstants; @@ -128,7 +129,7 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { // Device was updated and new firmware is different from previous firmware. send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis(), FIRMWARE); } - } else if (oldFirmwareId != null){ + } else if (oldFirmwareId != null) { // Device was updated and new firmware is not set. remove(device, FIRMWARE); } @@ -155,7 +156,7 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { // Device was updated and new firmware is different from previous firmware. send(device.getTenantId(), device.getId(), newSoftwareId, System.currentTimeMillis(), SOFTWARE); } - } else if (oldSoftwareId != null){ + } else if (oldSoftwareId != null) { // Device was updated and new firmware is not set. remove(device, SOFTWARE); } @@ -261,17 +262,23 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(getTargetTelemetryKey(firmware.getType(), TS), ts))); telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), OtaPackageUpdateStatus.QUEUED.name()))); - telemetryService.saveAndNotify(tenantId, deviceId, telemetry, new FutureCallback<>() { - @Override - public void onSuccess(@Nullable Void tmp) { - log.trace("[{}] Success save firmware status!", deviceId); - } + telemetryService.save(TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .entityId(deviceId) + .entries(telemetry) + .saveLatest(true) + .callback(new FutureCallback() { + @Override + public void onSuccess(@Nullable Void tmp) { + log.trace("[{}] Success save firmware status!", deviceId); + } - @Override - public void onFailure(Throwable t) { - log.error("[{}] Failed to save firmware status!", deviceId, t); - } - }); + @Override + public void onFailure(Throwable t) { + log.error("[{}] Failed to save firmware status!", deviceId, t); + } + }) + .build()); } @@ -282,19 +289,25 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(getTelemetryKey(otaPackageType, STATE), OtaPackageUpdateStatus.INITIATED.name())); - telemetryService.saveAndNotify(tenantId, deviceId, Collections.singletonList(status), new FutureCallback<>() { - @Override - public void onSuccess(@Nullable Void tmp) { - log.trace("[{}] Success save telemetry with target {} for device!", deviceId, otaPackage); - updateAttributes(device, otaPackage, ts, tenantId, deviceId, otaPackageType); - } + telemetryService.save(TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .entityId(deviceId) + .entries(Collections.singletonList(status)) + .saveLatest(true) + .callback(new FutureCallback() { + @Override + public void onSuccess(@Nullable Void tmp) { + log.trace("[{}] Success save telemetry with target {} for device!", deviceId, otaPackage); + updateAttributes(device, otaPackage, ts, tenantId, deviceId, otaPackageType); + } - @Override - public void onFailure(Throwable t) { - log.error("[{}] Failed to save telemetry with target {} for device!", deviceId, otaPackage, t); - updateAttributes(device, otaPackage, ts, tenantId, deviceId, otaPackageType); - } - }); + @Override + public void onFailure(Throwable t) { + log.error("[{}] Failed to save telemetry with target {} for device!", deviceId, otaPackage, t); + updateAttributes(device, otaPackage, ts, tenantId, deviceId, otaPackageType); + } + }) + .build()); } private void updateAttributes(Device device, OtaPackageInfo otaPackage, long ts, TenantId tenantId, DeviceId deviceId, OtaPackageType otaPackageType) { @@ -368,4 +381,5 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { } }); } + } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java index 50d8b9c371..9f7d4c0843 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java @@ -32,6 +32,7 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.thingsboard.common.util.DonAsynchron; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.EntityType; @@ -206,20 +207,28 @@ public abstract class AbstractBulkImportService { TenantProfile tenantProfile = tenantProfileCache.get(tenantId); long tenantTtl = TimeUnit.DAYS.toSeconds(((DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration()).getDefaultStorageTtlDays()); - tsSubscriptionService.saveAndNotify(tenantId, user.getCustomerId(), entityId, timeseries, tenantTtl, new FutureCallback() { - @Override - public void onSuccess(@Nullable Void tmp) { - entityActionService.logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, - ActionType.TIMESERIES_UPDATED, null, timeseries); - } - - @Override - public void onFailure(Throwable t) { - entityActionService.logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, - ActionType.TIMESERIES_UPDATED, BaseController.toException(t), timeseries); - throw new RuntimeException(t); - } - }); + tsSubscriptionService.save(TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .customerId(user.getCustomerId()) + .entityId(entityId) + .entries(timeseries) + .ttl(tenantTtl) + .saveLatest(true) + .callback(new FutureCallback() { + @Override + public void onSuccess(@Nullable Void tmp) { + entityActionService.logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, + ActionType.TIMESERIES_UPDATED, null, timeseries); + } + + @Override + public void onFailure(Throwable t) { + entityActionService.logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, + ActionType.TIMESERIES_UPDATED, BaseController.toException(t), timeseries); + throw new RuntimeException(t); + } + }) + .build()); }); } diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java index 5513c41929..682bddd436 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java @@ -28,6 +28,8 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.EntityType; @@ -70,7 +72,7 @@ import java.util.concurrent.Executors; */ @Service @Slf4j -public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionService implements TelemetrySubscriptionService { +public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionService implements TelemetrySubscriptionService, RuleEngineTelemetryService { private final AttributesService attrService; private final TimeseriesService tsService; @@ -115,39 +117,29 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer } @Override - public ListenableFuture saveAndNotify(TenantId tenantId, EntityId entityId, TsKvEntry ts) { + public ListenableFuture saveAndNotify(TimeseriesSaveRequest request) { SettableFuture future = SettableFuture.create(); - saveAndNotify(tenantId, entityId, Collections.singletonList(ts), new VoidFutureCallback(future)); + request.setCallback(new VoidFutureCallback(future)); + save(request); return future; } @Override - public void saveAndNotify(TenantId tenantId, EntityId entityId, List ts, FutureCallback callback) { - saveAndNotify(tenantId, null, entityId, ts, 0L, callback); - } - - @Override - public void saveAndNotify(TenantId tenantId, CustomerId customerId, EntityId entityId, List ts, long ttl, FutureCallback callback) { - doSaveAndNotify(tenantId, customerId, entityId, ts, ttl, callback, true); - } - - @Override - public void saveWithoutLatestAndNotify(TenantId tenantId, CustomerId customerId, EntityId entityId, List ts, long ttl, FutureCallback callback) { - doSaveAndNotify(tenantId, customerId, entityId, ts, ttl, callback, false); - } - - private void doSaveAndNotify(TenantId tenantId, CustomerId customerId, EntityId entityId, List ts, long ttl, FutureCallback callback, boolean saveLatest) { + public void save(TimeseriesSaveRequest request) { + TenantId tenantId = request.getTenantId(); + EntityId entityId = request.getEntityId(); checkInternalEntity(entityId); boolean sysTenant = TenantId.SYS_TENANT_ID.equals(tenantId) || tenantId == null; if (sysTenant || apiUsageStateService.getApiUsageState(tenantId).isDbStorageEnabled()) { - KvUtils.validate(ts, valueNoXssValidation); - if (saveLatest) { - saveAndNotifyInternal(tenantId, entityId, ts, ttl, getCallback(tenantId, customerId, sysTenant, callback)); + KvUtils.validate(request.getEntries(), valueNoXssValidation); + FutureCallback callback = getCallback(tenantId, request.getCustomerId(), sysTenant, request.getCallback()); + if (request.isSaveLatest()) { + saveAndNotifyInternal(tenantId, entityId, request.getEntries(), request.getTtl(), callback); } else { - saveWithoutLatestAndNotifyInternal(tenantId, entityId, ts, ttl, getCallback(tenantId, customerId, sysTenant, callback)); + saveWithoutLatestAndNotifyInternal(tenantId, entityId, request.getEntries(), request.getTtl(), callback); } } else { - callback.onFailure(new RuntimeException("DB storage writes are disabled due to API limits!")); + request.getCallback().onFailure(new RuntimeException("DB storage writes are disabled due to API limits!")); } } @@ -285,24 +277,12 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer addWsCallback(saveFuture, success -> onTimeSeriesUpdate(tenantId, entityId, ts)); } - @Override - public void deleteAndNotify(TenantId tenantId, EntityId entityId, String scope, List keys, FutureCallback callback) { - checkInternalEntity(entityId); - deleteAndNotifyInternal(tenantId, entityId, scope, keys, false, callback); - } - @Override public void deleteAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List keys, FutureCallback callback) { checkInternalEntity(entityId); deleteAndNotifyInternal(tenantId, entityId, scope, keys, false, callback); } - @Override - public void deleteAndNotify(TenantId tenantId, EntityId entityId, String scope, List keys, boolean notifyDevice, FutureCallback callback) { - checkInternalEntity(entityId); - deleteAndNotifyInternal(tenantId, entityId, scope, keys, notifyDevice, callback); - } - @Override public void deleteAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List keys, boolean notifyDevice, FutureCallback callback) { checkInternalEntity(entityId); @@ -358,12 +338,6 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer addWsCallback(deleteFuture, list -> onTimeSeriesDelete(tenantId, entityId, keys, list)); } - @Override - public void saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, long value, FutureCallback callback) { - saveAndNotify(tenantId, entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new LongDataEntry(key, value) - , System.currentTimeMillis())), callback); - } - @Override public void saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, long value, FutureCallback callback) { @@ -371,49 +345,24 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer , System.currentTimeMillis())), callback); } - @Override - public void saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, String value, FutureCallback callback) { - saveAndNotify(tenantId, entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry(key, value) - , System.currentTimeMillis())), callback); - } - @Override public void saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, String value, FutureCallback callback) { saveAndNotify(tenantId, entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry(key, value) , System.currentTimeMillis())), callback); } - @Override - public void saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, double value, FutureCallback callback) { - saveAndNotify(tenantId, entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new DoubleDataEntry(key, value) - , System.currentTimeMillis())), callback); - } - @Override public void saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, double value, FutureCallback callback) { saveAndNotify(tenantId, entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new DoubleDataEntry(key, value) , System.currentTimeMillis())), callback); } - @Override - public void saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, boolean value, FutureCallback callback) { - saveAndNotify(tenantId, entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new BooleanDataEntry(key, value) - , System.currentTimeMillis())), callback); - } - @Override public void saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, boolean value, FutureCallback callback) { saveAndNotify(tenantId, entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new BooleanDataEntry(key, value) , System.currentTimeMillis())), callback); } - @Override - public ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, long value) { - SettableFuture future = SettableFuture.create(); - saveAttrAndNotify(tenantId, entityId, scope, key, value, new VoidFutureCallback(future)); - return future; - } - @Override public ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, long value) { SettableFuture future = SettableFuture.create(); @@ -421,13 +370,6 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer return future; } - @Override - public ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, String value) { - SettableFuture future = SettableFuture.create(); - saveAttrAndNotify(tenantId, entityId, scope, key, value, new VoidFutureCallback(future)); - return future; - } - @Override public ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, String value) { SettableFuture future = SettableFuture.create(); @@ -435,13 +377,6 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer return future; } - @Override - public ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, double value) { - SettableFuture future = SettableFuture.create(); - saveAttrAndNotify(tenantId, entityId, scope, key, value, new VoidFutureCallback(future)); - return future; - } - @Override public ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, double value) { SettableFuture future = SettableFuture.create(); @@ -449,13 +384,6 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer return future; } - @Override - public ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, boolean value) { - SettableFuture future = SettableFuture.create(); - saveAttrAndNotify(tenantId, entityId, scope, key, value, new VoidFutureCallback(future)); - return future; - } - @Override public ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, boolean value) { SettableFuture future = SettableFuture.create(); @@ -560,6 +488,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer public void onFailure(Throwable t) { future.setException(t); } + } } diff --git a/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java b/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java index e3695d7efb..e32d6cbe5a 100644 --- a/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java @@ -29,6 +29,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.TestPropertySource; import org.testcontainers.shaded.org.apache.commons.lang3.RandomStringUtils; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmSeverity; @@ -804,19 +805,27 @@ public class WebsocketApiTest extends AbstractControllerTest { private void sendTelemetry(Device device, List tsData) throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); - tsService.saveAndNotify(device.getTenantId(), null, device.getId(), tsData, 0, new FutureCallback() { - @Override - public void onSuccess(@Nullable Void result) { - log.debug("sendTelemetry callback onSuccess"); - latch.countDown(); - } - - @Override - public void onFailure(Throwable t) { - log.error("Failed to send telemetry", t); - latch.countDown(); - } - }); + tsService.save(TimeseriesSaveRequest.builder() + .tenantId(device.getTenantId()) + .customerId(null) + .entityId(device.getId()) + .entries(tsData) + .ttl(0) + .saveLatest(true) + .callback(new FutureCallback() { + @Override + public void onSuccess(@Nullable Void result) { + log.debug("sendTelemetry callback onSuccess"); + latch.countDown(); + } + + @Override + public void onFailure(Throwable t) { + log.error("Failed to send telemetry", t); + latch.countDown(); + } + }) + .build()); assertThat(latch.await(TIMEOUT, TimeUnit.SECONDS)).as("await sendTelemetry callback"); } @@ -841,4 +850,5 @@ public class WebsocketApiTest extends AbstractControllerTest { }); assertThat(latch.await(TIMEOUT, TimeUnit.SECONDS)).as("await sendAttributes callback").isTrue(); } + } diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java index 0c2de91b2f..9f9a928839 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java @@ -18,7 +18,6 @@ package org.thingsboard.rule.engine.api; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.AttributeScope; -import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; @@ -33,13 +32,9 @@ import java.util.List; */ public interface RuleEngineTelemetryService { - ListenableFuture saveAndNotify(TenantId tenantId, EntityId entityId, TsKvEntry ts); + void save(TimeseriesSaveRequest request); - void saveAndNotify(TenantId tenantId, EntityId entityId, List ts, FutureCallback callback); - - void saveAndNotify(TenantId tenantId, CustomerId id, EntityId entityId, List ts, long ttl, FutureCallback callback); - - void saveWithoutLatestAndNotify(TenantId tenantId, CustomerId id, EntityId entityId, List ts, long ttl, FutureCallback callback); + ListenableFuture saveAndNotify(TimeseriesSaveRequest request); @Deprecated(since = "3.7.0") void saveAndNotify(TenantId tenantId, EntityId entityId, String scope, List attributes, FutureCallback callback); @@ -53,54 +48,24 @@ public interface RuleEngineTelemetryService { void saveLatestAndNotify(TenantId tenantId, EntityId entityId, List ts, FutureCallback callback); - @Deprecated(since = "3.7.0") - ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, long value); - ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, long value); - @Deprecated(since = "3.7.0") - ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, String value); - ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, String value); - @Deprecated(since = "3.7.0") - ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, double value); - ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, double value); - @Deprecated(since = "3.7.0") - ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, boolean value); - ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, boolean value); - @Deprecated(since = "3.7.0") - void saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, long value, FutureCallback callback); - void saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, long value, FutureCallback callback); - @Deprecated(since = "3.7.0") - void saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, String value, FutureCallback callback); - void saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, String value, FutureCallback callback); - @Deprecated(since = "3.7.0") - void saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, double value, FutureCallback callback); - void saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, double value, FutureCallback callback); - @Deprecated(since = "3.7.0") - void saveAttrAndNotify(TenantId tenantId, EntityId entityId, String scope, String key, boolean value, FutureCallback callback); - void saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, boolean value, FutureCallback callback); - @Deprecated(since = "3.7.0") - void deleteAndNotify(TenantId tenantId, EntityId entityId, String scope, List keys, FutureCallback callback); - void deleteAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List keys, FutureCallback callback); - @Deprecated(since = "3.7.0") - void deleteAndNotify(TenantId tenantId, EntityId entityId, String scope, List keys, boolean notifyDevice, FutureCallback callback); - void deleteAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List keys, boolean notifyDevice, FutureCallback callback); void deleteLatest(TenantId tenantId, EntityId entityId, List keys, FutureCallback callback); @@ -108,4 +73,5 @@ public interface RuleEngineTelemetryService { void deleteAllLatest(TenantId tenantId, EntityId entityId, FutureCallback> callback); void deleteTimeseriesAndNotify(TenantId tenantId, EntityId entityId, List keys, List deleteTsKvQueries, FutureCallback callback); + } diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java new file mode 100644 index 0000000000..042915c95b --- /dev/null +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java @@ -0,0 +1,102 @@ +/** + * 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. + */ +package org.thingsboard.rule.engine.api; + +import com.google.common.util.concurrent.FutureCallback; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Data; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.TsKvEntry; + +import java.util.List; + +@Data +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class TimeseriesSaveRequest { + private final TenantId tenantId; + private final CustomerId customerId; + private final EntityId entityId; + private final List entries; + private final long ttl; + private final boolean saveLatest; + private FutureCallback callback; + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private TenantId tenantId; + private CustomerId customerId; + private EntityId entityId; + private List entries; + private long ttl; + private FutureCallback callback; + private boolean saveLatest; + + Builder() {} + + public Builder tenantId(TenantId tenantId) { + this.tenantId = tenantId; + return this; + } + + public Builder customerId(CustomerId customerId) { + this.customerId = customerId; + return this; + } + + public Builder entityId(EntityId entityId) { + this.entityId = entityId; + return this; + } + + public Builder entries(List entries) { + this.entries = entries; + return this; + } + + public Builder entry(TsKvEntry entry) { + this.entries = List.of(entry); + return this; + } + + public Builder ttl(long ttl) { + this.ttl = ttl; + return this; + } + + public Builder saveLatest(boolean saveLatest) { + this.saveLatest = saveLatest; + return this; + } + + public Builder callback(FutureCallback callback) { + this.callback = callback; + return this; + } + + public TimeseriesSaveRequest build() { + return new TimeseriesSaveRequest(tenantId, customerId, entityId, entries, ttl, saveLatest, callback); + } + + } + +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java index 4e64ad854e..83d82534a7 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java @@ -29,6 +29,7 @@ import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.rule.engine.util.SemaphoreWithTbMsgQueue; import org.thingsboard.server.common.data.AttributeScope; @@ -42,6 +43,7 @@ import org.thingsboard.server.common.msg.TbMsg; import java.math.BigDecimal; import java.math.RoundingMode; +import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.concurrent.ConcurrentMap; @@ -139,9 +141,13 @@ public class TbMathNode implements TbNode { } private ListenableFuture saveTimeSeries(TbContext ctx, TbMsg msg, double result, TbMathResult mathResultDef) { - - return ctx.getTelemetryService().saveAndNotify(ctx.getTenantId(), msg.getOriginator(), - new BasicTsKvEntry(System.currentTimeMillis(), new DoubleDataEntry(mathResultDef.getKey(), result))); + final BasicTsKvEntry basicTsKvEntry = new BasicTsKvEntry(System.currentTimeMillis(), new DoubleDataEntry(mathResultDef.getKey(), result)); + return ctx.getTelemetryService().saveAndNotify(TimeseriesSaveRequest.builder() + .tenantId(ctx.getTenantId()) + .entityId(msg.getOriginator()) + .entries(Collections.singletonList(basicTsKvEntry)) + .saveLatest(true) + .build()); } private ListenableFuture saveAttribute(TbContext ctx, TbMsg msg, double result, TbMathResult mathResultDef) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java index c90e8e2375..b0d5e23b50 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java @@ -22,6 +22,7 @@ import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.data.StringUtils; @@ -104,11 +105,15 @@ public class TbMsgTimeseriesNode implements TbNode { if (ttl == 0L) { ttl = tenantProfileDefaultStorageTtl; } - if (config.isSkipLatestPersistence()) { - ctx.getTelemetryService().saveWithoutLatestAndNotify(ctx.getTenantId(), msg.getCustomerId(), msg.getOriginator(), tsKvEntryList, ttl, new TelemetryNodeCallback(ctx, msg)); - } else { - ctx.getTelemetryService().saveAndNotify(ctx.getTenantId(), msg.getCustomerId(), msg.getOriginator(), tsKvEntryList, ttl, new TelemetryNodeCallback(ctx, msg)); - } + ctx.getTelemetryService().save(TimeseriesSaveRequest.builder() + .tenantId(ctx.getTenantId()) + .customerId(msg.getCustomerId()) + .entityId(msg.getOriginator()) + .entries(tsKvEntryList) + .ttl(ttl) + .saveLatest(!config.isSkipLatestPersistence()) + .callback(new TelemetryNodeCallback(ctx, msg)) + .build()); } public static long computeTs(TbMsg msg, boolean ignoreMetadataTs) { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java index ba81fbad20..137a51d394 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java @@ -47,7 +47,6 @@ import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.DoubleDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; -import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -73,6 +72,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyDouble; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.assertArg; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.willAnswer; import static org.mockito.BDDMockito.willReturn; @@ -460,14 +460,16 @@ public class TbMathNodeTest { ); TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 5).toString()); - when(telemetryService.saveAndNotify(any(), any(), any(TsKvEntry.class))) - .thenReturn(Futures.immediateFuture(null)); + when(telemetryService.saveAndNotify(any())).thenReturn(Futures.immediateFuture(null)); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); - verify(telemetryService, times(1)).saveAndNotify(any(), any(), any(TsKvEntry.class)); + verify(telemetryService, times(1)).saveAndNotify(assertArg(request -> { + assertThat(request.getEntries()).size().isOne(); + assertThat(request.isSaveLatest()).isTrue(); + })); TbMsg resultMsg = msgCaptor.getValue(); assertNotNull(resultMsg); @@ -485,14 +487,16 @@ public class TbMathNodeTest { ); TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 5).toString()); - when(telemetryService.saveAndNotify(any(), any(), any(TsKvEntry.class))) - .thenReturn(Futures.immediateFuture(null)); + when(telemetryService.saveAndNotify(any())).thenReturn(Futures.immediateFuture(null)); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); - verify(telemetryService, times(1)).saveAndNotify(any(), any(), any(TsKvEntry.class)); + verify(telemetryService, times(1)).saveAndNotify(assertArg(request -> { + assertThat(request.getEntries()).size().isOne(); + assertThat(request.isSaveLatest()).isTrue(); + })); TbMsg resultMsg = msgCaptor.getValue(); assertNotNull(resultMsg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java index 1fb4e9eefd..d80a0bd340 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java @@ -31,6 +31,7 @@ import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.id.DeviceId; @@ -54,10 +55,8 @@ import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyList; -import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.assertArg; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -127,19 +126,23 @@ public class TbMsgTimeseriesNodeTest { when(ctxMock.getTelemetryService()).thenReturn(telemetryServiceMock); when(ctxMock.getTenantId()).thenReturn(TENANT_ID); doAnswer(invocation -> { - TelemetryNodeCallback callback = invocation.getArgument(5); - callback.onSuccess(null); + TimeseriesSaveRequest request = invocation.getArgument(0); + request.getCallback().onSuccess(null); return null; - }).when(telemetryServiceMock).saveAndNotify(any(), any(), any(), anyList(), anyLong(), any()); + }).when(telemetryServiceMock).save(any()); node.onMsg(ctxMock, msg); List expectedList = getTsKvEntriesListWithTs(data, System.currentTimeMillis()); - ArgumentCaptor> entryListCaptor = ArgumentCaptor.forClass(List.class); - verify(telemetryServiceMock).saveAndNotify(eq(TENANT_ID), isNull(), eq(DEVICE_ID), entryListCaptor.capture(), - eq(tenantProfileDefaultStorageTtl), any(TelemetryNodeCallback.class)); - assertThat(entryListCaptor.getValue()).usingRecursiveFieldByFieldElementComparatorIgnoringFields("ts") - .containsExactlyElementsOf(expectedList); + verify(telemetryServiceMock).save(assertArg(request -> { + assertThat(request.getTenantId()).isEqualTo(TENANT_ID); + assertThat(request.getCustomerId()).isNull(); + assertThat(request.getEntityId()).isEqualTo(DEVICE_ID); + assertThat(request.getEntries()).usingRecursiveFieldByFieldElementComparatorIgnoringFields("ts").containsExactlyElementsOf(expectedList); + assertThat(request.getTtl()).isEqualTo(tenantProfileDefaultStorageTtl); + assertThat(request.isSaveLatest()).isTrue(); + assertThat(request.getCallback()).isInstanceOf(TelemetryNodeCallback.class); + })); verify(ctxMock).tellSuccess(msg); verifyNoMoreInteractions(ctxMock, telemetryServiceMock); } @@ -164,18 +167,23 @@ public class TbMsgTimeseriesNodeTest { when(ctxMock.getTelemetryService()).thenReturn(telemetryServiceMock); when(ctxMock.getTenantId()).thenReturn(TENANT_ID); doAnswer(invocation -> { - TelemetryNodeCallback callback = invocation.getArgument(5); - callback.onSuccess(null); + TimeseriesSaveRequest request = invocation.getArgument(0); + request.getCallback().onSuccess(null); return null; - }).when(telemetryServiceMock).saveWithoutLatestAndNotify(any(), any(), any(), anyList(), anyLong(), any()); + }).when(telemetryServiceMock).save(any()); node.onMsg(ctxMock, msg); List expectedList = getTsKvEntriesListWithTs(data, ts); - ArgumentCaptor> entryListCaptor = ArgumentCaptor.forClass(List.class); - verify(telemetryServiceMock).saveWithoutLatestAndNotify( - eq(TENANT_ID), isNull(), eq(DEVICE_ID), entryListCaptor.capture(), eq(ttlFromConfig), any(TelemetryNodeCallback.class)); - assertThat(entryListCaptor.getValue()).containsExactlyElementsOf(expectedList); + verify(telemetryServiceMock).save(assertArg(request -> { + assertThat(request.getTenantId()).isEqualTo(TENANT_ID); + assertThat(request.getCustomerId()).isNull(); + assertThat(request.getEntityId()).isEqualTo(DEVICE_ID); + assertThat(request.getEntries()).containsExactlyElementsOf(expectedList); + assertThat(request.getTtl()).isEqualTo(ttlFromConfig); + assertThat(request.isSaveLatest()).isFalse(); + assertThat(request.getCallback()).isInstanceOf(TelemetryNodeCallback.class); + })); verify(ctxMock).tellSuccess(msg); verifyNoMoreInteractions(ctxMock, telemetryServiceMock); } @@ -200,7 +208,14 @@ public class TbMsgTimeseriesNodeTest { TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metadata, data); node.onMsg(ctxMock, msg); - verify(telemetryServiceMock).saveAndNotify(eq(TENANT_ID), isNull(), eq(DEVICE_ID), anyList(), eq(expectedTtl), any(TelemetryNodeCallback.class)); + verify(telemetryServiceMock).save(assertArg(request -> { + assertThat(request.getTenantId()).isEqualTo(TENANT_ID); + assertThat(request.getCustomerId()).isNull(); + assertThat(request.getEntityId()).isEqualTo(DEVICE_ID); + assertThat(request.getTtl()).isEqualTo(expectedTtl); + assertThat(request.isSaveLatest()).isTrue(); + assertThat(request.getCallback()).isInstanceOf(TelemetryNodeCallback.class); + })); } private static Stream givenTtlFromConfigAndTtlFromMd_whenOnMsg_thenVerifyTtl() { From 6c0bca2baef4f5a0f03e04f0d12f63a1f6f90436 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 17 Dec 2024 12:25:57 +0200 Subject: [PATCH 012/108] Refactor saveAndNotifyInternal for timeseries; save latest by default --- .../controller/TelemetryController.java | 1 - .../DefaultTbApiUsageStateService.java | 33 +++++-- .../service/edge/rpc/EdgeGrpcService.java | 7 +- .../ota/DefaultOtaPackageStateService.java | 7 +- .../state/DefaultDeviceStateService.java | 23 +++-- .../DefaultRuleEngineStatisticsService.java | 22 +++-- .../csv/AbstractBulkImportService.java | 3 +- .../system/DefaultSystemInfoService.java | 13 ++- .../DefaultTelemetrySubscriptionService.java | 87 +++++++++---------- .../telemetry/InternalTelemetryService.java | 6 +- .../server/controller/WebsocketApiTest.java | 3 - .../engine/api/TimeseriesSaveRequest.java | 9 +- .../rule/engine/math/TbMathNode.java | 4 +- 13 files changed, 126 insertions(+), 92 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java index 8c63be6de9..f72b9684e2 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java @@ -679,7 +679,6 @@ public class TelemetryController extends BaseController { .entityId(entityId) .entries(entries) .ttl(tenantTtl) - .saveLatest(true) .callback(new FutureCallback() { @Override public void onSuccess(@Nullable Void tmp) { diff --git a/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java b/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java index 60bf529ace..d9c5077ab7 100644 --- a/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java @@ -27,6 +27,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.rule.engine.api.MailService; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.data.ApiFeature; import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.ApiUsageRecordState; @@ -91,9 +92,9 @@ import java.util.stream.Collectors; public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService implements TbApiUsageStateService { public static final String HOURLY = "Hourly"; - public static final FutureCallback VOID_CALLBACK = new FutureCallback() { + public static final FutureCallback VOID_CALLBACK = new FutureCallback() { @Override - public void onSuccess(@Nullable Integer result) { + public void onSuccess(@Nullable Void result) { } @Override @@ -214,7 +215,12 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService updateLock.unlock(); } log.trace("[{}][{}] Saving new stats: {}", tenantId, ownerId, updatedEntries); - tsWsService.saveAndNotifyInternal(tenantId, usageState.getApiUsageState().getId(), updatedEntries, VOID_CALLBACK); + tsWsService.saveInternal(TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .entityId(usageState.getApiUsageState().getId()) + .entries(updatedEntries) + .callback(VOID_CALLBACK) + .build()); if (!result.isEmpty()) { persistAndNotify(usageState, result); } @@ -321,7 +327,12 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService } } if (!profileThresholds.isEmpty()) { - tsWsService.saveAndNotifyInternal(tenantId, id, profileThresholds, VOID_CALLBACK); + tsWsService.saveInternal(TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .entityId(id) + .entries(profileThresholds) + .callback(VOID_CALLBACK) + .build()); } } @@ -348,7 +359,12 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService long ts = System.currentTimeMillis(); List stateTelemetry = new ArrayList<>(); result.forEach((apiFeature, aState) -> stateTelemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(apiFeature.getApiStateKey(), aState.name())))); - tsWsService.saveAndNotifyInternal(state.getTenantId(), state.getApiUsageState().getId(), stateTelemetry, VOID_CALLBACK); + tsWsService.saveInternal(TimeseriesSaveRequest.builder() + .tenantId(state.getTenantId()) + .entityId(state.getApiUsageState().getId()) + .entries(stateTelemetry) + .callback(VOID_CALLBACK) + .build()); if (state.getEntityType() == EntityType.TENANT && !state.getEntityId().equals(TenantId.SYS_TENANT_ID)) { String email = tenantService.findTenantById(state.getTenantId()).getEmail(); @@ -436,7 +452,12 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService .map(key -> new BasicTsKvEntry(state.getCurrentCycleTs(), new LongDataEntry(key.getApiCountKey(), 0L))) .collect(Collectors.toList()); - tsWsService.saveAndNotifyInternal(state.getTenantId(), state.getApiUsageState().getId(), counts, VOID_CALLBACK); + tsWsService.saveInternal(TimeseriesSaveRequest.builder() + .tenantId(state.getTenantId()) + .entityId(state.getApiUsageState().getId()) + .entries(counts) + .callback(VOID_CALLBACK) + .build()); } BaseApiUsageState getOrFetchState(TenantId tenantId, EntityId ownerId) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java index d451bb36b3..dd2216dd5b 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java @@ -69,7 +69,6 @@ import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; import java.io.IOException; import java.io.InputStream; -import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -507,8 +506,7 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i tsSubService.save(TimeseriesSaveRequest.builder() .tenantId(tenantId) .entityId(edgeId) - .entries(Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry(key, value)))) - .saveLatest(true) + .entry(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry(key, value))) .callback(new AttributeSaveCallback(tenantId, edgeId, key, value)) .build()); } else { @@ -522,8 +520,7 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i tsSubService.save(TimeseriesSaveRequest.builder() .tenantId(tenantId) .entityId(edgeId) - .entries(Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new BooleanDataEntry(key, value)))) - .saveLatest(true) + .entry(new BasicTsKvEntry(System.currentTimeMillis(), new BooleanDataEntry(key, value))) .callback(new AttributeSaveCallback(tenantId, edgeId, key, value)) .build()); } else { diff --git a/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java b/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java index 78da4eee7f..afae615333 100644 --- a/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java @@ -55,7 +55,6 @@ import org.thingsboard.server.queue.provider.TbCoreQueueFactory; import org.thingsboard.server.queue.provider.TbRuleEngineQueueFactory; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -266,7 +265,6 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { .tenantId(tenantId) .entityId(deviceId) .entries(telemetry) - .saveLatest(true) .callback(new FutureCallback() { @Override public void onSuccess(@Nullable Void tmp) { @@ -292,9 +290,8 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { telemetryService.save(TimeseriesSaveRequest.builder() .tenantId(tenantId) .entityId(deviceId) - .entries(Collections.singletonList(status)) - .saveLatest(true) - .callback(new FutureCallback() { + .entry(status) + .callback(new FutureCallback<>() { @Override public void onSuccess(@Nullable Void tmp) { log.trace("[{}] Success save telemetry with target {} for device!", deviceId, otaPackage); diff --git a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java index 6025e874b9..4f230f9d6b 100644 --- a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java @@ -38,6 +38,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.AttributeScope; @@ -866,10 +867,13 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService(deviceId, key, value)); + tsSubService.saveInternal(TimeseriesSaveRequest.builder() + .tenantId(TenantId.SYS_TENANT_ID) + .entityId(deviceId) + .entry(new BasicTsKvEntry(getCurrentTimeMillis(), new LongDataEntry(key, value))) + .ttl(telemetryTtl) + .callback(new TelemetrySaveCallback<>(deviceId, key, value)) + .build()); } else { tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, deviceId, AttributeScope.SERVER_SCOPE, key, value, new TelemetrySaveCallback<>(deviceId, key, value)); } @@ -877,10 +881,13 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService(deviceId, key, value)); + tsSubService.saveInternal(TimeseriesSaveRequest.builder() + .tenantId(TenantId.SYS_TENANT_ID) + .entityId(deviceId) + .entry(new BasicTsKvEntry(getCurrentTimeMillis(), new BooleanDataEntry(key, value))) + .ttl(telemetryTtl) + .callback(new TelemetrySaveCallback<>(deviceId, key, value)) + .build()); } else { tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, deviceId, AttributeScope.SERVER_SCOPE, key, value, new TelemetrySaveCallback<>(deviceId, key, value)); } diff --git a/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java b/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java index e0c78e0afa..2a214388a1 100644 --- a/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java +++ b/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java @@ -22,6 +22,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.data.id.QueueStatsId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; @@ -37,7 +38,6 @@ import org.thingsboard.server.queue.util.TbRuleEngineComponent; import org.thingsboard.server.service.queue.TbRuleEngineConsumerStats; import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; -import java.util.Collections; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -53,9 +53,9 @@ import java.util.stream.Collectors; public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsService { public static final String RULE_ENGINE_EXCEPTION = "ruleEngineException"; - public static final FutureCallback CALLBACK = new FutureCallback() { + public static final FutureCallback CALLBACK = new FutureCallback() { @Override - public void onSuccess(@Nullable Integer result) { + public void onSuccess(@Nullable Void result) { } @@ -89,7 +89,13 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS if (!tsList.isEmpty()) { long ttl = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getQueueStatsTtlDays); ttl = TimeUnit.DAYS.toSeconds(ttl); - tsService.saveAndNotifyInternal(tenantId, queueStatsId, tsList, ttl, CALLBACK); + tsService.saveInternal(TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .entityId(queueStatsId) + .entries(tsList) + .ttl(ttl) + .callback(CALLBACK) + .build()); } } } catch (Exception e) { @@ -103,7 +109,13 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS TsKvEntry tsKv = new BasicTsKvEntry(e.getTs(), new JsonDataEntry(RULE_ENGINE_EXCEPTION, e.toJsonString(maxErrorMessageLength))); long ttl = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getRuleEngineExceptionsTtlDays); ttl = TimeUnit.DAYS.toSeconds(ttl); - tsService.saveAndNotifyInternal(tenantId, getQueueStatsId(tenantId, queueName), Collections.singletonList(tsKv), ttl, CALLBACK); + tsService.saveInternal(TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .entityId(getQueueStatsId(tenantId, queueName)) + .entry(tsKv) + .ttl(ttl) + .callback(CALLBACK) + .build()); } catch (Exception e2) { if (!"Asset is referencing to non-existent tenant!".equalsIgnoreCase(e2.getMessage())) { log.debug("[{}] Failed to store the statistics", tenantId, e2); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java index 9f7d4c0843..6d04c3173f 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java @@ -213,8 +213,7 @@ public abstract class AbstractBulkImportService() { + .callback(new FutureCallback<>() { @Override public void onSuccess(@Nullable Void tmp) { entityActionService.logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, diff --git a/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java b/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java index 4016352f2d..b59c723996 100644 --- a/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java +++ b/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java @@ -27,6 +27,7 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.rule.engine.api.SmsService; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.FeaturesInfo; @@ -71,9 +72,9 @@ import static org.thingsboard.common.util.SystemUtil.getTotalMemory; @Slf4j public class DefaultSystemInfoService extends TbApplicationEventListener implements SystemInfoService { - public static final FutureCallback CALLBACK = new FutureCallback<>() { + public static final FutureCallback CALLBACK = new FutureCallback<>() { @Override - public void onSuccess(@Nullable Integer result) { + public void onSuccess(@Nullable Void result) { } @Override @@ -200,7 +201,13 @@ public class DefaultSystemInfoService extends TbApplicationEventListener telemetry) { ApiUsageState apiUsageState = apiUsageStateClient.getApiUsageState(TenantId.SYS_TENANT_ID); - telemetryService.saveAndNotifyInternal(TenantId.SYS_TENANT_ID, apiUsageState.getId(), telemetry, systemInfoTtlSeconds, CALLBACK); + telemetryService.saveInternal(TimeseriesSaveRequest.builder() + .tenantId(TenantId.SYS_TENANT_ID) + .entityId(apiUsageState.getId()) + .entries(telemetry) + .ttl(systemInfoTtlSeconds) + .callback(CALLBACK) + .build()); } private List getSystemData(ServiceInfo serviceInfo) { diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java index 682bddd436..1ce0550ee2 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java @@ -116,14 +116,6 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer super.shutdownExecutor(); } - @Override - public ListenableFuture saveAndNotify(TimeseriesSaveRequest request) { - SettableFuture future = SettableFuture.create(); - request.setCallback(new VoidFutureCallback(future)); - save(request); - return future; - } - @Override public void save(TimeseriesSaveRequest request) { TenantId tenantId = request.getTenantId(); @@ -132,51 +124,39 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer boolean sysTenant = TenantId.SYS_TENANT_ID.equals(tenantId) || tenantId == null; if (sysTenant || apiUsageStateService.getApiUsageState(tenantId).isDbStorageEnabled()) { KvUtils.validate(request.getEntries(), valueNoXssValidation); - FutureCallback callback = getCallback(tenantId, request.getCustomerId(), sysTenant, request.getCallback()); - if (request.isSaveLatest()) { - saveAndNotifyInternal(tenantId, entityId, request.getEntries(), request.getTtl(), callback); - } else { - saveWithoutLatestAndNotifyInternal(tenantId, entityId, request.getEntries(), request.getTtl(), callback); - } + FutureCallback callback = getApiUsageCallback(tenantId, request.getCustomerId(), sysTenant, request.getCallback()); + ListenableFuture future = saveInternal(request); + Futures.addCallback(future, callback, tsCallBackExecutor); } else { request.getCallback().onFailure(new RuntimeException("DB storage writes are disabled due to API limits!")); } } - private FutureCallback getCallback(TenantId tenantId, CustomerId customerId, boolean sysTenant, FutureCallback callback) { - return new FutureCallback<>() { - @Override - public void onSuccess(Integer result) { - if (!sysTenant && result != null && result > 0) { - apiUsageClient.report(tenantId, customerId, ApiUsageRecordKey.STORAGE_DP_COUNT, result); - } - callback.onSuccess(null); - } - - @Override - public void onFailure(Throwable t) { - callback.onFailure(t); - } - }; - } - @Override - public void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, List ts, FutureCallback callback) { - saveAndNotifyInternal(tenantId, entityId, ts, 0L, callback); + public ListenableFuture saveAndNotify(TimeseriesSaveRequest request) { + SettableFuture future = SettableFuture.create(); + request.setCallback(new VoidFutureCallback(future)); + save(request); + return future; } @Override - public void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, List ts, long ttl, FutureCallback callback) { - ListenableFuture saveFuture = tsService.save(tenantId, entityId, ts, ttl); - addMainCallback(saveFuture, callback); - addWsCallback(saveFuture, success -> onTimeSeriesUpdate(tenantId, entityId, ts)); - addEntityViewCallback(tenantId, entityId, ts); - } + public ListenableFuture saveInternal(TimeseriesSaveRequest request) { + TenantId tenantId = request.getTenantId(); + EntityId entityId = request.getEntityId(); + ListenableFuture saveFuture; + if (request.isSaveLatest()) { + saveFuture = tsService.save(tenantId, entityId, request.getEntries(), request.getTtl()); + } else { + saveFuture = tsService.saveWithoutLatest(tenantId, entityId, request.getEntries(), request.getTtl()); + } - private void saveWithoutLatestAndNotifyInternal(TenantId tenantId, EntityId entityId, List ts, long ttl, FutureCallback callback) { - ListenableFuture saveFuture = tsService.saveWithoutLatest(tenantId, entityId, ts, ttl); - addMainCallback(saveFuture, callback); - addWsCallback(saveFuture, success -> onTimeSeriesUpdate(tenantId, entityId, ts)); + addMainCallback(saveFuture, request.getCallback()); + addWsCallback(saveFuture, success -> onTimeSeriesUpdate(tenantId, entityId, request.getEntries())); + if (request.isSaveLatest()) { + addEntityViewCallback(tenantId, entityId, request.getEntries()); + } + return saveFuture; } private void addEntityViewCallback(TenantId tenantId, EntityId entityId, List ts) { @@ -452,11 +432,11 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer }, tsCallBackExecutor); } - private void addMainCallback(ListenableFuture saveFuture, final FutureCallback callback) { + private void addMainCallback(ListenableFuture saveFuture, final FutureCallback callback) { Futures.addCallback(saveFuture, new FutureCallback() { @Override public void onSuccess(@Nullable S result) { - callback.onSuccess(result); + callback.onSuccess(null); } @Override @@ -472,6 +452,23 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer } } + private FutureCallback getApiUsageCallback(TenantId tenantId, CustomerId customerId, boolean sysTenant, FutureCallback callback) { + return new FutureCallback<>() { + @Override + public void onSuccess(Integer result) { + if (!sysTenant && result != null && result > 0) { + apiUsageClient.report(tenantId, customerId, ApiUsageRecordKey.STORAGE_DP_COUNT, result); + } + callback.onSuccess(null); + } + + @Override + public void onFailure(Throwable t) { + callback.onFailure(t); + } + }; + } + private static class VoidFutureCallback implements FutureCallback { private final SettableFuture future; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java index a6dc6d1aae..c861efb46f 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java @@ -16,7 +16,9 @@ package org.thingsboard.server.service.telemetry; import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -30,9 +32,7 @@ import java.util.List; */ public interface InternalTelemetryService extends RuleEngineTelemetryService { - void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, List ts, FutureCallback callback); - - void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, List ts, long ttl, FutureCallback callback); + ListenableFuture saveInternal(TimeseriesSaveRequest request); @Deprecated(since = "3.7.0") void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, String scope, List attributes, boolean notifyDevice, FutureCallback callback); diff --git a/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java b/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java index e32d6cbe5a..40c7e435ca 100644 --- a/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java @@ -807,11 +807,8 @@ public class WebsocketApiTest extends AbstractControllerTest { CountDownLatch latch = new CountDownLatch(1); tsService.save(TimeseriesSaveRequest.builder() .tenantId(device.getTenantId()) - .customerId(null) .entityId(device.getId()) .entries(tsData) - .ttl(0) - .saveLatest(true) .callback(new FutureCallback() { @Override public void onSuccess(@Nullable Void result) { diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java index 042915c95b..bcf021d3ca 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java @@ -18,7 +18,8 @@ package org.thingsboard.rule.engine.api; import com.google.common.util.concurrent.FutureCallback; import lombok.AccessLevel; import lombok.AllArgsConstructor; -import lombok.Data; +import lombok.Getter; +import lombok.Setter; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -26,15 +27,17 @@ import org.thingsboard.server.common.data.kv.TsKvEntry; import java.util.List; -@Data +@Getter @AllArgsConstructor(access = AccessLevel.PRIVATE) public class TimeseriesSaveRequest { + private final TenantId tenantId; private final CustomerId customerId; private final EntityId entityId; private final List entries; private final long ttl; private final boolean saveLatest; + @Setter private FutureCallback callback; public static Builder builder() { @@ -49,7 +52,7 @@ public class TimeseriesSaveRequest { private List entries; private long ttl; private FutureCallback callback; - private boolean saveLatest; + private boolean saveLatest = true; Builder() {} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java index 83d82534a7..893b06bfec 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java @@ -43,7 +43,6 @@ import org.thingsboard.server.common.msg.TbMsg; import java.math.BigDecimal; import java.math.RoundingMode; -import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.concurrent.ConcurrentMap; @@ -145,8 +144,7 @@ public class TbMathNode implements TbNode { return ctx.getTelemetryService().saveAndNotify(TimeseriesSaveRequest.builder() .tenantId(ctx.getTenantId()) .entityId(msg.getOriginator()) - .entries(Collections.singletonList(basicTsKvEntry)) - .saveLatest(true) + .entry(basicTsKvEntry) .build()); } From 9b131bece6f5c0196aa334f5e1be1d070be0f218 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Tue, 17 Dec 2024 12:29:32 +0200 Subject: [PATCH 013/108] UI: Oil and gas symbols with bundle --- .../json/system/scada_symbols/crane-hp.svg | 346 +++++++++++++++++ .../json/system/scada_symbols/drawwork-hp.svg | 355 +++++++++++++++++ .../json/system/scada_symbols/drill-hp.svg | 353 +++++++++++++++++ .../system/scada_symbols/drilling-line-hp.svg | 361 ++++++++++++++++++ .../system/scada_symbols/drilling-rig-hp.svg | 354 +++++++++++++++++ .../json/system/scada_symbols/hook-hp.svg | 292 ++++++++++++++ .../json/system/scada_symbols/platform-hp.svg | 266 +++++++++++++ .../system/scada_symbols/preventer-hp.svg | 342 +++++++++++++++++ .../json/system/scada_symbols/rotor-hp.svg | 345 +++++++++++++++++ ...eneral_high_performance_scada_symbols.json | 4 +- .../high_performance_scada_oil_gas.json | 20 + .../assets/locale/locale.constant-en_US.json | 3 +- 12 files changed, 3039 insertions(+), 2 deletions(-) create mode 100644 application/src/main/data/json/system/scada_symbols/crane-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/drawwork-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/drill-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/drilling-line-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/drilling-rig-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/hook-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/platform-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/preventer-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/rotor-hp.svg create mode 100644 application/src/main/data/json/system/widget_bundles/high_performance_scada_oil_gas.json diff --git a/application/src/main/data/json/system/scada_symbols/crane-hp.svg b/application/src/main/data/json/system/scada_symbols/crane-hp.svg new file mode 100644 index 0000000000..7e7c64c99c --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/crane-hp.svg @@ -0,0 +1,346 @@ +{ + "title": "HP Crane", + "description": "Crane with various states.", + "searchTags": [ + "crane" + ], + "widgetSizeX": 5, + "widgetSizeY": 6, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "required": null, + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "required": null, + "subLabel": "{i18n:scada.symbol.stopped}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "required": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "required": null, + "subLabel": "{i18n:scada.symbol.critical}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + } + ] +} + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/drawwork-hp.svg b/application/src/main/data/json/system/scada_symbols/drawwork-hp.svg new file mode 100644 index 0000000000..bb98d8cb5e --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/drawwork-hp.svg @@ -0,0 +1,355 @@ +{ + "title": "HP Drawwork", + "description": "Drawwork with various states.", + "searchTags": [ + "drawwork" + ], + "widgetSizeX": 3, + "widgetSizeY": 2, + "tags": [ + { + "tag": "circle-background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = '#dedede';\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "drawwork-background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "required": null, + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "required": null, + "subLabel": "{i18n:scada.symbol.stopped}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "required": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "required": null, + "subLabel": "{i18n:scada.symbol.critical}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + } + ] +} + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/drill-hp.svg b/application/src/main/data/json/system/scada_symbols/drill-hp.svg new file mode 100644 index 0000000000..9f620109d5 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/drill-hp.svg @@ -0,0 +1,353 @@ +{ + "title": "HP Drill", + "description": "Drill with various states.", + "searchTags": [ + "drill" + ], + "widgetSizeX": 1, + "widgetSizeY": 1, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "required": null, + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "required": null, + "subLabel": "{i18n:scada.symbol.stopped}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "required": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "required": null, + "subLabel": "{i18n:scada.symbol.critical}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + } + ] +} + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/drilling-line-hp.svg b/application/src/main/data/json/system/scada_symbols/drilling-line-hp.svg new file mode 100644 index 0000000000..60bce1a645 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/drilling-line-hp.svg @@ -0,0 +1,361 @@ +{ + "title": "HP Drilling line", + "description": "Drilling line with various states.", + "searchTags": [ + "drilling line" + ], + "widgetSizeX": 1, + "widgetSizeY": 2, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "secondary-background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = '#dedede';\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "required": null, + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "required": null, + "subLabel": "{i18n:scada.symbol.stopped}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "required": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "required": null, + "subLabel": "{i18n:scada.symbol.critical}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + } + ] +} + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/drilling-rig-hp.svg b/application/src/main/data/json/system/scada_symbols/drilling-rig-hp.svg new file mode 100644 index 0000000000..76c9af6454 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/drilling-rig-hp.svg @@ -0,0 +1,354 @@ +{ + "title": "HP Drilling rig", + "description": "Drilling rig with various states.", + "searchTags": [ + "drilling rig" + ], + "widgetSizeX": 4, + "widgetSizeY": 9, + "tags": [ + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "drilling-ring", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "required": null, + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "required": null, + "subLabel": "{i18n:scada.symbol.stopped}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "required": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "required": null, + "subLabel": "{i18n:scada.symbol.critical}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + } + ] +} + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/hook-hp.svg b/application/src/main/data/json/system/scada_symbols/hook-hp.svg new file mode 100644 index 0000000000..9fdcf86208 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/hook-hp.svg @@ -0,0 +1,292 @@ +{ + "title": "HP Hook", + "description": "Hook with various states.", + "searchTags": [ + "hook" + ], + "widgetSizeX": 1, + "widgetSizeY": 2, + "tags": [ + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "hook", + "stateRenderFunction": "element.attr({fill: ctx.properties.hookColor});", + "actions": null + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "hookColor", + "name": "{i18n:scada.symbol.hook-color}", + "type": "color", + "default": "#FFFFFF", + "required": null, + "subLabel": "", + "divider": false, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "required": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "required": null, + "subLabel": "{i18n:scada.symbol.critical}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + } + ] +} + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/platform-hp.svg b/application/src/main/data/json/system/scada_symbols/platform-hp.svg new file mode 100644 index 0000000000..2592be9d56 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/platform-hp.svg @@ -0,0 +1,266 @@ +{ + "title": "HP Platform", + "description": "Platform with various states.", + "searchTags": [ + "platform" + ], + "widgetSizeX": 6, + "widgetSizeY": 3, + "tags": [ + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "warningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FAA405", + "required": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#D12730", + "required": null, + "subLabel": "{i18n:scada.symbol.critical}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + } + ] +} + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/preventer-hp.svg b/application/src/main/data/json/system/scada_symbols/preventer-hp.svg new file mode 100644 index 0000000000..a72d974241 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/preventer-hp.svg @@ -0,0 +1,342 @@ +{ + "title": "HP Preventer", + "description": "Preventer with various states.", + "searchTags": [ + "preventer" + ], + "widgetSizeX": 2, + "widgetSizeY": 1, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "required": null, + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "required": null, + "subLabel": "{i18n:scada.symbol.stopped}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "required": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "required": null, + "subLabel": "{i18n:scada.symbol.critical}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + } + ] +} + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/rotor-hp.svg b/application/src/main/data/json/system/scada_symbols/rotor-hp.svg new file mode 100644 index 0000000000..095831a89e --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/rotor-hp.svg @@ -0,0 +1,345 @@ +{ + "title": "HP Rotor", + "description": "Rotor with various states.", + "searchTags": [ + "rotor" + ], + "widgetSizeX": 2, + "widgetSizeY": 1, + "tags": [ + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "rotor-background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "required": null, + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "required": null, + "subLabel": "{i18n:scada.symbol.stopped}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "required": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "required": null, + "subLabel": "{i18n:scada.symbol.critical}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + } + ] +} + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/general_high_performance_scada_symbols.json b/application/src/main/data/json/system/widget_bundles/general_high_performance_scada_symbols.json index ffb0fee835..5083384432 100644 --- a/application/src/main/data/json/system/widget_bundles/general_high_performance_scada_symbols.json +++ b/application/src/main/data/json/system/widget_bundles/general_high_performance_scada_symbols.json @@ -26,6 +26,8 @@ "hp_bottom_tee_connector", "hp_right_tee_connector", "hp_left_tee_connector", - "hp_top_tee_connector" + "hp_top_tee_connector", + "hp_drawwork", + "hp_crane" ] } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/high_performance_scada_oil_gas.json b/application/src/main/data/json/system/widget_bundles/high_performance_scada_oil_gas.json new file mode 100644 index 0000000000..a2f798c4a4 --- /dev/null +++ b/application/src/main/data/json/system/widget_bundles/high_performance_scada_oil_gas.json @@ -0,0 +1,20 @@ +{ + "widgetsBundle": { + "alias": "high_performance_scada_oil_gas", + "title": "High-performance SCADA oil & gas", + "image": "tb-image:aHBfc2NhZGFfb2lsX2dhc19zeXN0ZW1fYnVuZGxlX2ltYWdlLnBuZw==:IkhpZ2gtcGVyZm9ybWFuY2UgU0NBREEgb2lsICYgZ2FzIiBzeXN0ZW0gYnVuZGxlIGltYWdl:SU1BR0U=;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAb1BMVEXe3t7b29vf398AAADf39/e3t7e3t7////ExMTGxsbj4+O5ubnQ0NDU1NStra3S0tKhoaHx8fGVlZWpqanV1dWysrK+vr6mpqa2trbLy8vBwcHa2trX19e7u7uampqKioqvr6+jo6Oenp77+/t6enr5/tYxAAAABnRSTlPvIK8Av79l/pT7AAAI50lEQVR42uza3W6jMBCG4f3T5zFjbCeIQKqkTVZ7/9e4yGEzAopYYyqgu+9BoO1JH40LxM2X719/fMHe+/b1+5ev+2c0NYxv+BR9gmXVjuSzQPAfsrX+bQhTycRMhO00A+IYoEs4ZWymeIh1aCKECOn9Knr9wnTpEEfXC4AzHjGSq1SvCpOlQwwBlwUk13vBt7LWTVkfkukmOp2KuvSQloU4IEgE4jCnF/VXvUBaFkLh9XyBQZvBsx1B+I9H3ylpbVX5YZqR5UdIi0IIj0qAXJjNucSsjLVVXXD2PiFnrkrPBEgLQ+TIdDbn1OuWfh+iEVHC0jIecAG1Vwih6UJwJbuLSYAYtqExiH1kIC0KATeOcwsypllb5xMiI1vaMvytT3bQp2vt6SMgDpdr55JLhJiKW52pyHK6vS4PMRd0IYhKq1npxSH46ToQj31CPJF1EIdleuMdQhgGsDjjkdEG3uwRQo/Xk2XAsGE0DmhEVBUnyuMQGd3rcvGJtCDDTBrBAUZkfCtudZ6Fxq67WSivr8WdIC04ETnqhwOEORkKjd0QKWQgLQoxXUhwwI1KNvuI4rgD8WbiaWuzEO4ssaPMZ07EvmLOR/7AuaitJUBaEkLPEwN4Fl98nrODmuiQ6w96z65FRN7oUcjm3+qyQDxBWGjbIcQb0BPiZkh8WVxL0lnT4d2bSF6/2Lq6QloO4hza3gwA2wfGZ6hpuEE3cQtJhjDaNIdXkrWV0pvu9YaIUiAaJIeOMKnjETElQPTzQM9jyCKm9SH85FDnBBoxbQSiIZCnhBDR6hByADQEIqeMiFaHGAAafQhodxAGNIYQ0N6Wlu09lMhXO5sIafQg8hOHv291iEcPMnO7cW0I0zjEWkS0MoRAYxBLjIjWnghAXYg4sKulNdgOEgf2NBFqXwQiDkRdtjYwEYAgEHFEjWRlCA8Go1vHziAkJwIJjtgn+Y1MBCCB8Jx3u5uBgP5A2GB/EAOJHhBxRG0JrQtxDEgWTd50JxbRihAGeiPh7rf2MpH+b01sNPY4Ed2DMGGfEEaos/f7CZaW7P0mXLZWgTgHSF72fpdaW8eqen2tqkhLPIQ7DiN7vwutLZerUE6IKBHijez9LgWxBxU6MCZKhOiOo7/3m76TfVNtd0yVPhFxCAQEsSZUR3wKOxEiDoF0JQ7z+6naPnhpkRNHDwIS7PxOqq3ERGkQI44BBOzSIXXEv3FTICyOIQT+IVnkIynaYaIkiBVHHyISxuw4kw9nYaIkCImjAxFJ2paQV888JkqCsDgGEJEQZlbdqKipqSjoVmFGAolwCKQnYcyLMqUKUk11oVR2xUjpEBLHACKS2ZBKKVZFgCieuCcmTkQcQ4hICLM6al1kSj8gWaH1K0ZKh/xu706U04aBMAD3XO1hWQhwCLRNerz/O1YRcly8GFFb6rid/i0Yk5l2v6xsK4IJg0NDBkkLsyLmRYEmBOPDTGcXQJ6DIw+BrYMZORyMIb+LHaEnH7ZdBzplIO4uCAr8fg6eAoBYTIgw0QtLHyZFIALiMpDZL+52bCLEeAkbNvKyZ5SkDAQhSLIQnDWTP5kICYr4N0H03LFQRwbJJARnXRGdPUPCzbIPO48RomYqpSCXkhYFQwTbC8ccCHGs+ylsOCTc7ypCGriUtOoB4MwlIRcbEK+G5D2ZYAqP1CS4BGQoT3AKsuCtdA/GWCI0ESMm7lk1dSwDkWFI9VtE+XVoLXi5R6w5hzkgODOZLwEZJKk7CKr8WedfF0u3NNwb14FKAQjBWNJQA81mcCyBAH4/nbrdjmMnnuMOgk7BjgySYHBpWBVbpOu+WX7IzuBLQAZJe15WaaIFi61kNwj5LIA4ByOJnM/I6Ej1QKBIKkB0bS1KutgDqrGEUCr1IdBuAUBEHMio7nVDCEYZzsIb/SUolvodgb4TDlQIyqU+BHG7lbbdIoKKgyKpABFd2gZTtqAiUCBVIA2otOpBnzVDVGW3IQjFUh+C2CLGOxhlzR3ByScJQYcclEr9jgCeIa2WlBlb5SETjtQRaFc0tnIQvPpUgmjJeiGqrtGbM1cz2/pNyOTbZVPW2xG8tj9AtMQ5uDvNQyP7Pe1E9iq0l/MtfU3KdgRhBNESuVdxOg4vgVrL5jJs6HwjE0OLINqhIEoi9zEe3ahw4poQHO0piJbQPY4jGB2qCGlhSLMBDdEShHwObK7Ecz2IaIeGJMn9EGcmwtUgzcihIFrishK86MLFYW4rQRwqh4bENM3dR3vnX8uOCm8tDZI6EFEOBdESzPTjR18qeWMpKKw31FPYVoMMDg3RknxHZFh+Z+vTCja/Huh1IKQcCqIkGQhyP6zYmtd4MtwLqnREORRES/CehnifKkwJe5SEVSDKoSBa4m5JXKpzZ8RchJltIpaHOKccGqIlAtN5SGUZq64hvi+5PES0Q0O0xOWvIdbra7tNvbAVINqhIVqCMJ1j/+uNjIYYW6sjpB0aoiUt3MhQs+5I6oUv3xHt0BAtIZgOJQhpiGebtsUhbXDkIVqCOQiHPzo2PVkcIluYA4FGchA09k92pIF5ELgBadL33E4c7FXOWjIXgjCZz6noKxBcH0Ty1xGjJcRcCYJzIeRgKl8SxNPYISY9xevpCGD2x1wyI4n3PglO8JITdNCE20kWQwRykAVjy7OXkSPtf1Gzs6UQrAJxvq88XjdSLPvHNLCOxSECNYYWpILPbyu1aeY1/HSyh78F4hqTwi+GEDaeuOchrGlo3VzJposlX7IULKZ3dLCqjoDAjXRspvII5SGiqh/Sprs+zW9B4GlC4h1UgKjijjyZI4yS+W8fflxzcAc1IC2M8slM5lOmIyp4ujKsCKpAZAkEIdMSJjIXsdZ3dSDNEojD/LIpU1pojCfhuKkBcW4WJD+2OhE54DlPz7uQ513aPYjIsTREYBEEMytb0//UyiDTHWnpZr7CKBuKeZoLoeHhl7TlyViI2StIwRToiG5FvjEIM1IFssHXHPnOnPA18gck/z/YcdX5D1lb/pkP1n3zAf6JvPt3PkT7/du//2O033x8+/4nILYzAoIqAxwAAAAASUVORK5CYII=", + "scada": true, + "description": "Bundle with high-performance SCADA symbols for oil and gas system", + "order": 9405, + "name": "High-performance SCADA oil & gas" + }, + "widgetTypeFqns": [ + "hp_drilling_rig", + "hp_hook", + "hp_rotor", + "hp_preventer", + "hp_drill", + "hp_drilling_line", + "hp_platform" + ] +} \ No newline at end of file diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index c2f45da84f..017f0fc269 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3308,7 +3308,8 @@ "low-critical-scale": "Low Critical state", "low-critical-state-hint": "Double value indicates a low critical range up to the min value scale.", "colors": "Colors", - "alarm-colors": "Alarm colors" + "alarm-colors": "Alarm colors", + "hook-color": "Hook color" } }, "item": { From e8cf3179c53b83b43bdbe777e36b19b1956654d6 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 17 Dec 2024 15:45:05 +0200 Subject: [PATCH 014/108] Refactor saveAndNotify and saveAndNotifyInternal for attributes --- .../controller/TelemetryController.java | 33 ++- .../device/ClaimDevicesServiceImpl.java | 16 +- .../service/edge/rpc/EdgeGrpcService.java | 22 +- .../telemetry/BaseTelemetryProcessor.java | 43 ++-- .../DefaultTbEntityViewService.java | 66 ++--- .../ota/DefaultOtaPackageStateService.java | 27 +- .../state/DefaultDeviceStateService.java | 42 +-- .../csv/AbstractBulkImportService.java | 39 +-- .../impl/BaseEntityImportService.java | 25 +- .../DefaultTelemetrySubscriptionService.java | 186 +++++--------- .../telemetry/InternalTelemetryService.java | 7 +- .../server/controller/WebsocketApiTest.java | 33 ++- .../state/DefaultDeviceStateServiceTest.java | 242 +++++++++++------- .../engine/api/AttributesSaveRequest.java | 115 +++++++++ .../api/RuleEngineTelemetryService.java | 27 +- .../engine/api/TimeseriesSaveRequest.java | 9 +- .../TbCopyAttributesToEntityViewNode.java | 10 +- .../rule/engine/math/TbMathNode.java | 15 +- .../engine/telemetry/TbMsgAttributesNode.java | 31 ++- .../TbCopyAttributesToEntityViewNodeTest.java | 39 +-- .../rule/engine/math/TbMathNodeTest.java | 11 +- .../telemetry/TbMsgAttributesNodeTest.java | 20 +- .../telemetry/TbMsgTimeseriesNodeTest.java | 11 +- 23 files changed, 619 insertions(+), 450 deletions(-) create mode 100644 rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/AttributesSaveRequest.java diff --git a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java index f72b9684e2..b6d854399c 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java @@ -47,6 +47,7 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.request.async.DeferredResult; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.data.AttributeScope; @@ -625,19 +626,25 @@ public class TelemetryController extends BaseController { } SecurityUser user = getCurrentUser(); return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_ATTRIBUTES, entityIdSrc, (result, tenantId, entityId) -> { - tsSubService.saveAndNotify(tenantId, entityId, scope, attributes, new FutureCallback() { - @Override - public void onSuccess(@Nullable Void tmp) { - logAttributesUpdated(user, entityId, scope, attributes, null); - result.setResult(new ResponseEntity(HttpStatus.OK)); - } - - @Override - public void onFailure(Throwable t) { - logAttributesUpdated(user, entityId, scope, attributes, t); - AccessValidator.handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR); - } - }); + tsSubService.save(AttributesSaveRequest.builder() + .tenantId(tenantId) + .entityId(entityId) + .scope(scope) + .entries(attributes) + .callback(new FutureCallback<>() { + @Override + public void onSuccess(@Nullable Void tmp) { + logAttributesUpdated(user, entityId, scope, attributes, null); + result.setResult(new ResponseEntity(HttpStatus.OK)); + } + + @Override + public void onFailure(Throwable t) { + logAttributesUpdated(user, entityId, scope, attributes, t); + AccessValidator.handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR); + } + }) + .build()); }); } else { return getImmediateDeferredResult("Request is not a JSON object", HttpStatus.BAD_REQUEST); diff --git a/application/src/main/java/org/thingsboard/server/service/device/ClaimDevicesServiceImpl.java b/application/src/main/java/org/thingsboard/server/service/device/ClaimDevicesServiceImpl.java index a77b7d0874..cd3e096ad8 100644 --- a/application/src/main/java/org/thingsboard/server/service/device/ClaimDevicesServiceImpl.java +++ b/application/src/main/java/org/thingsboard/server/service/device/ClaimDevicesServiceImpl.java @@ -28,6 +28,7 @@ import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.Customer; @@ -37,7 +38,6 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BooleanDataEntry; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.customer.CustomerService; @@ -178,11 +178,12 @@ public class ClaimDevicesServiceImpl implements ClaimDevicesService { return Futures.immediateFuture(new ReclaimResult(unassignedCustomer)); } SettableFuture result = SettableFuture.create(); - telemetryService.saveAndNotify( - tenantId, savedDevice.getId(), AttributeScope.SERVER_SCOPE, List.of( - new BaseAttributeKvEntry(new BooleanDataEntry(CLAIM_ATTRIBUTE_NAME, true), System.currentTimeMillis()) - ), - new FutureCallback<>() { + telemetryService.save(AttributesSaveRequest.builder() + .tenantId(tenantId) + .entityId(savedDevice.getId()) + .scope(AttributeScope.SERVER_SCOPE) + .entry(new BooleanDataEntry(CLAIM_ATTRIBUTE_NAME, true)) + .callback(new FutureCallback<>() { @Override public void onSuccess(@Nullable Void tmp) { result.set(new ReclaimResult(unassignedCustomer)); @@ -192,7 +193,8 @@ public class ClaimDevicesServiceImpl implements ClaimDevicesService { public void onFailure(Throwable t) { result.setException(t); } - }); + }) + .build()); return result; } cacheEviction(device.getId()); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java index dd2216dd5b..7551302d33 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java @@ -32,6 +32,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.cache.TbTransactionalCache; import org.thingsboard.server.cluster.TbClusterService; @@ -42,7 +43,6 @@ import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.BooleanDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.msg.TbMsgType; @@ -506,11 +506,17 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i tsSubService.save(TimeseriesSaveRequest.builder() .tenantId(tenantId) .entityId(edgeId) - .entry(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry(key, value))) + .entry(new LongDataEntry(key, value)) .callback(new AttributeSaveCallback(tenantId, edgeId, key, value)) .build()); } else { - tsSubService.saveAttrAndNotify(tenantId, edgeId, AttributeScope.SERVER_SCOPE, key, value, new AttributeSaveCallback(tenantId, edgeId, key, value)); + tsSubService.save(AttributesSaveRequest.builder() + .tenantId(tenantId) + .entityId(edgeId) + .scope(AttributeScope.SERVER_SCOPE) + .entry(new LongDataEntry(key, value)) + .callback(new AttributeSaveCallback(tenantId, edgeId, key, value)) + .build()); } } @@ -520,11 +526,17 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i tsSubService.save(TimeseriesSaveRequest.builder() .tenantId(tenantId) .entityId(edgeId) - .entry(new BasicTsKvEntry(System.currentTimeMillis(), new BooleanDataEntry(key, value))) + .entry(new BooleanDataEntry(key, value)) .callback(new AttributeSaveCallback(tenantId, edgeId, key, value)) .build()); } else { - tsSubService.saveAttrAndNotify(tenantId, edgeId, AttributeScope.SERVER_SCOPE, key, value, new AttributeSaveCallback(tenantId, edgeId, key, value)); + tsSubService.save(AttributesSaveRequest.builder() + .tenantId(tenantId) + .entityId(edgeId) + .scope(AttributeScope.SERVER_SCOPE) + .entry(new BooleanDataEntry(key, value)) + .callback(new AttributeSaveCallback(tenantId, edgeId, key, value)) + .build()); } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java index d94d4d8939..9ef40280c9 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java @@ -31,6 +31,7 @@ import org.apache.commons.lang3.tuple.Pair; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.DataConstants; @@ -277,16 +278,29 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { JsonObject json = JsonUtils.getJsonObject(msg.getKvList()); List attributes = new ArrayList<>(JsonConverter.convertToAttributes(json)); String scope = metaData.getValue("scope"); - tsSubService.saveAndNotify(tenantId, entityId, AttributeScope.valueOf(scope), attributes, new FutureCallback() { - @Override - public void onSuccess(@Nullable Void tmp) { - var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); - TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), TbMsgType.ATTRIBUTES_UPDATED, entityId, - customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null); - edgeCtx.getClusterService().pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { + tsSubService.save(AttributesSaveRequest.builder() + .tenantId(tenantId) + .entityId(entityId) + .scope(AttributeScope.valueOf(scope)) + .entries(attributes) + .callback(new FutureCallback<>() { @Override - public void onSuccess(TbQueueMsgMetadata metadata) { - futureToSet.set(null); + public void onSuccess(@Nullable Void tmp) { + var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); + TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), TbMsgType.ATTRIBUTES_UPDATED, entityId, + customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null); + edgeCtx.getClusterService().pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { + @Override + public void onSuccess(TbQueueMsgMetadata metadata) { + futureToSet.set(null); + } + + @Override + public void onFailure(Throwable t) { + log.error("[{}] Can't process attributes update [{}]", tenantId, msg, t); + futureToSet.setException(t); + } + }); } @Override @@ -294,15 +308,8 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { log.error("[{}] Can't process attributes update [{}]", tenantId, msg, t); futureToSet.setException(t); } - }); - } - - @Override - public void onFailure(Throwable t) { - log.error("[{}] Can't process attributes update [{}]", tenantId, msg, t); - futureToSet.setException(t); - } - }); + }) + .build()); return futureToSet; } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java index 82877283cd..790b1086b4 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java @@ -25,6 +25,7 @@ import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.util.ConcurrentReferenceHashMap; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntityType; @@ -273,36 +274,41 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen return Futures.transform(getAttrFuture, attributeKvEntries -> { List attributes; if (attributeKvEntries != null && !attributeKvEntries.isEmpty()) { - attributes = - attributeKvEntries.stream() - .filter(attributeKvEntry -> { - long startTime = entityView.getStartTimeMs(); - long endTime = entityView.getEndTimeMs(); - long lastUpdateTs = attributeKvEntry.getLastUpdateTs(); - return startTime == 0 && endTime == 0 || - (endTime == 0 && startTime < lastUpdateTs) || - (startTime == 0 && endTime > lastUpdateTs) || - (startTime < lastUpdateTs && endTime > lastUpdateTs); - }).collect(Collectors.toList()); - tsSubService.saveAndNotify(entityView.getTenantId(), entityId, scope, attributes, new FutureCallback() { - @Override - public void onSuccess(@Nullable Void tmp) { - try { - logAttributesUpdated(entityView.getTenantId(), user, entityId, scope, attributes, null); - } catch (ThingsboardException e) { - log.error("Failed to log attribute updates", e); - } - } - - @Override - public void onFailure(Throwable t) { - try { - logAttributesUpdated(entityView.getTenantId(), user, entityId, scope, attributes, t); - } catch (ThingsboardException e) { - log.error("Failed to log attribute updates", e); - } - } - }); + attributes = attributeKvEntries.stream() + .filter(attributeKvEntry -> { + long startTime = entityView.getStartTimeMs(); + long endTime = entityView.getEndTimeMs(); + long lastUpdateTs = attributeKvEntry.getLastUpdateTs(); + return startTime == 0 && endTime == 0 || + (endTime == 0 && startTime < lastUpdateTs) || + (startTime == 0 && endTime > lastUpdateTs) || + (startTime < lastUpdateTs && endTime > lastUpdateTs); + }).collect(Collectors.toList()); + tsSubService.save(AttributesSaveRequest.builder() + .tenantId(entityView.getTenantId()) + .entityId(entityId) + .scope(scope) + .entries(attributes) + .callback(new FutureCallback<>() { + @Override + public void onSuccess(@Nullable Void tmp) { + try { + logAttributesUpdated(entityView.getTenantId(), user, entityId, scope, attributes, null); + } catch (ThingsboardException e) { + log.error("Failed to log attribute updates", e); + } + } + + @Override + public void onFailure(Throwable t) { + try { + logAttributesUpdated(entityView.getTenantId(), user, entityId, scope, attributes, t); + } catch (ThingsboardException e) { + log.error("Failed to log attribute updates", e); + } + } + }) + .build()); } return null; }, MoreExecutors.directExecutor()); diff --git a/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java b/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java index afae615333..3db4e53379 100644 --- a/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java @@ -20,6 +20,7 @@ import jakarta.annotation.Nullable; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.cluster.TbClusterService; @@ -346,17 +347,23 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { remove(device, otaPackageType, attrToRemove); - telemetryService.saveAndNotify(tenantId, deviceId, AttributeScope.SHARED_SCOPE, attributes, new FutureCallback<>() { - @Override - public void onSuccess(@Nullable Void tmp) { - log.trace("[{}] Success save attributes with target firmware!", deviceId); - } + telemetryService.save(AttributesSaveRequest.builder() + .tenantId(tenantId) + .entityId(deviceId) + .scope(AttributeScope.SHARED_SCOPE) + .entries(attributes) + .callback(new FutureCallback<>() { + @Override + public void onSuccess(@Nullable Void tmp) { + log.trace("[{}] Success save attributes with target firmware!", deviceId); + } - @Override - public void onFailure(Throwable t) { - log.error("[{}] Failed to save attributes with target firmware!", deviceId, t); - } - }); + @Override + public void onFailure(Throwable t) { + log.error("[{}] Failed to save attributes with target firmware!", deviceId, t); + } + }) + .build()); } private void remove(Device device, OtaPackageType otaPackageType) { diff --git a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java index 4f230f9d6b..8280cf44b8 100644 --- a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java @@ -38,6 +38,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.ApiUsageRecordKey; @@ -52,6 +53,7 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.BooleanDataEntry; import org.thingsboard.server.common.data.kv.KvEntry; @@ -866,30 +868,30 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService(deviceId, key, value)) - .build()); - } else { - tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, deviceId, AttributeScope.SERVER_SCOPE, key, value, new TelemetrySaveCallback<>(deviceId, key, value)); - } + save(deviceId, new LongDataEntry(key, value), getCurrentTimeMillis()); } private void save(DeviceId deviceId, String key, boolean value) { + save(deviceId, new BooleanDataEntry(key, value), getCurrentTimeMillis()); + } + + private void save(DeviceId deviceId, KvEntry kvEntry, long ts) { if (persistToTelemetry) { tsSubService.saveInternal(TimeseriesSaveRequest.builder() .tenantId(TenantId.SYS_TENANT_ID) .entityId(deviceId) - .entry(new BasicTsKvEntry(getCurrentTimeMillis(), new BooleanDataEntry(key, value))) + .entry(new BasicTsKvEntry(ts, kvEntry)) .ttl(telemetryTtl) - .callback(new TelemetrySaveCallback<>(deviceId, key, value)) + .callback(new TelemetrySaveCallback<>(deviceId, kvEntry)) .build()); } else { - tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, deviceId, AttributeScope.SERVER_SCOPE, key, value, new TelemetrySaveCallback<>(deviceId, key, value)); + tsSubService.save(AttributesSaveRequest.builder() + .tenantId(TenantId.SYS_TENANT_ID) + .entityId(deviceId) + .scope(AttributeScope.SERVER_SCOPE) + .entry(new BaseAttributeKvEntry(ts, kvEntry)) + .callback(new TelemetrySaveCallback<>(deviceId, kvEntry)) + .build()); } } @@ -899,23 +901,21 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService implements FutureCallback { private final DeviceId deviceId; - private final String key; - private final Object value; + private final KvEntry kvEntry; - TelemetrySaveCallback(DeviceId deviceId, String key, Object value) { + TelemetrySaveCallback(DeviceId deviceId, KvEntry kvEntry) { this.deviceId = deviceId; - this.key = key; - this.value = value; + this.kvEntry = kvEntry; } @Override public void onSuccess(@Nullable T result) { - log.trace("[{}] Successfully updated attribute [{}] with value [{}]", deviceId, key, value); + log.trace("[{}] Successfully updated entry {}", deviceId, kvEntry); } @Override public void onFailure(Throwable t) { - log.warn("[{}] Failed to update attribute [{}] with value [{}]", deviceId, key, value, t); + log.warn("[{}] Failed to update entry {}", deviceId, kvEntry, t); } } } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java index 6d04c3173f..f3e8266ce6 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java @@ -32,6 +32,7 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.thingsboard.common.util.DonAsynchron; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.data.AttributeScope; @@ -237,23 +238,27 @@ public abstract class AbstractBulkImportService attributes = new ArrayList<>(JsonConverter.convertToAttributes(kvsEntry.getValue())); accessValidator.validateEntityAndCallback(user, Operation.WRITE_ATTRIBUTES, entity.getId(), (result, tenantId, entityId) -> { - tsSubscriptionService.saveAndNotify(tenantId, entityId, AttributeScope.valueOf(scope), attributes, new FutureCallback<>() { - - @Override - public void onSuccess(Void unused) { - entityActionService.logEntityAction(user, (UUIDBased & EntityId) entityId, null, - null, ActionType.ATTRIBUTES_UPDATED, null, AttributeScope.valueOf(scope), attributes); - } - - @Override - public void onFailure(Throwable throwable) { - entityActionService.logEntityAction(user, (UUIDBased & EntityId) entityId, null, - null, ActionType.ATTRIBUTES_UPDATED, BaseController.toException(throwable), - AttributeScope.valueOf(scope), attributes); - throw new RuntimeException(throwable); - } - - }); + tsSubscriptionService.save(AttributesSaveRequest.builder() + .tenantId(tenantId) + .entityId(entityId) + .scope(AttributeScope.valueOf(scope)) + .entries(attributes) + .callback(new FutureCallback<>() { + @Override + public void onSuccess(Void unused) { + entityActionService.logEntityAction(user, (UUIDBased & EntityId) entityId, null, + null, ActionType.ATTRIBUTES_UPDATED, null, AttributeScope.valueOf(scope), attributes); + } + + @Override + public void onFailure(Throwable throwable) { + entityActionService.logEntityAction(user, (UUIDBased & EntityId) entityId, null, + null, ActionType.ATTRIBUTES_UPDATED, BaseController.toException(throwable), + AttributeScope.valueOf(scope), attributes); + throw new RuntimeException(throwable); + } + }) + .build()); }); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java index f88844bd09..199f54cd12 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java @@ -24,6 +24,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; @@ -257,16 +258,22 @@ public abstract class BaseEntityImportService() { - @Override - public void onSuccess(@Nullable Void unused) { - } + tsSubService.save(AttributesSaveRequest.builder() + .tenantId(user.getTenantId()) + .entityId(entity.getId()) + .scope(scope) + .entries(attributeKvEntries) + .callback(new FutureCallback<>() { + @Override + public void onSuccess(@Nullable Void unused) { + } - @Override - public void onFailure(Throwable thr) { - log.error("Failed to import attributes for {} {}", entity.getId().getEntityType(), entity.getId(), thr); - } - }); + @Override + public void onFailure(Throwable thr) { + log.error("Failed to import attributes for {} {}", entity.getId().getEntityType(), entity.getId(), thr); + } + }) + .build()); }); }); } diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java index 1ce0550ee2..bc78139d2f 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java @@ -28,6 +28,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.data.ApiUsageRecordKey; @@ -38,12 +39,7 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; -import org.thingsboard.server.common.data.kv.BooleanDataEntry; import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; -import org.thingsboard.server.common.data.kv.DoubleDataEntry; -import org.thingsboard.server.common.data.kv.LongDataEntry; -import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvLatestRemovingResult; import org.thingsboard.server.common.msg.queue.TbCallback; @@ -57,7 +53,6 @@ import org.thingsboard.server.service.subscription.TbSubscriptionUtils; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; @@ -159,89 +154,18 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer return saveFuture; } - private void addEntityViewCallback(TenantId tenantId, EntityId entityId, List ts) { - if (EntityType.DEVICE.equals(entityId.getEntityType()) || EntityType.ASSET.equals(entityId.getEntityType())) { - Futures.addCallback(this.tbEntityViewService.findEntityViewsByTenantIdAndEntityIdAsync(tenantId, entityId), - new FutureCallback<>() { - @Override - public void onSuccess(@Nullable List result) { - if (result != null && !result.isEmpty()) { - Map> tsMap = new HashMap<>(); - for (TsKvEntry entry : ts) { - tsMap.computeIfAbsent(entry.getKey(), s -> new ArrayList<>()).add(entry); - } - for (EntityView entityView : result) { - List keys = entityView.getKeys() != null && entityView.getKeys().getTimeseries() != null ? - entityView.getKeys().getTimeseries() : new ArrayList<>(tsMap.keySet()); - List entityViewLatest = new ArrayList<>(); - long startTs = entityView.getStartTimeMs(); - long endTs = entityView.getEndTimeMs() == 0 ? Long.MAX_VALUE : entityView.getEndTimeMs(); - for (String key : keys) { - List entries = tsMap.get(key); - if (entries != null) { - Optional tsKvEntry = entries.stream() - .filter(entry -> entry.getTs() > startTs && entry.getTs() <= endTs) - .max(Comparator.comparingLong(TsKvEntry::getTs)); - tsKvEntry.ifPresent(entityViewLatest::add); - } - } - if (!entityViewLatest.isEmpty()) { - saveLatestAndNotify(tenantId, entityView.getId(), entityViewLatest, new FutureCallback<>() { - @Override - public void onSuccess(@Nullable Void tmp) { - } - - @Override - public void onFailure(Throwable t) { - } - }); - } - } - } - } - - @Override - public void onFailure(Throwable t) { - log.error("Error while finding entity views by tenantId and entityId", t); - } - }, MoreExecutors.directExecutor()); - } - } - - @Override - public void saveAndNotify(TenantId tenantId, EntityId entityId, String scope, List attributes, FutureCallback callback) { - saveAndNotify(tenantId, entityId, scope, attributes, true, callback); - } - @Override - public void saveAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributes, FutureCallback callback) { - saveAndNotify(tenantId, entityId, scope, attributes, true, callback); + public void save(AttributesSaveRequest request) { + checkInternalEntity(request.getEntityId()); + saveInternal(request); } @Override - public void saveAndNotify(TenantId tenantId, EntityId entityId, String scope, List attributes, boolean notifyDevice, FutureCallback callback) { - checkInternalEntity(entityId); - saveAndNotifyInternal(tenantId, entityId, scope, attributes, notifyDevice, callback); - } - - @Override - public void saveAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributes, boolean notifyDevice, FutureCallback callback) { - checkInternalEntity(entityId); - saveAndNotifyInternal(tenantId, entityId, scope, attributes, notifyDevice, callback); - } - - @Override - public void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, String scope, List attributes, boolean notifyDevice, FutureCallback callback) { - ListenableFuture> saveFuture = attrService.save(tenantId, entityId, scope, attributes); - addVoidCallback(saveFuture, callback); - addWsCallback(saveFuture, success -> onAttributesUpdate(tenantId, entityId, scope, attributes, notifyDevice)); - } - - @Override - public void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributes, boolean notifyDevice, FutureCallback callback) { - ListenableFuture> saveFuture = attrService.save(tenantId, entityId, scope, attributes); - addVoidCallback(saveFuture, callback); - addWsCallback(saveFuture, success -> onAttributesUpdate(tenantId, entityId, scope.name(), attributes, notifyDevice)); + public void saveInternal(AttributesSaveRequest request) { + log.trace("Executing saveInternal [{}]", request); + ListenableFuture> saveFuture = attrService.save(request.getTenantId(), request.getEntityId(), request.getScope(), request.getEntries()); + addVoidCallback(saveFuture, request.getCallback()); + addWsCallback(saveFuture, success -> onAttributesUpdate(request.getTenantId(), request.getEntityId(), request.getScope().name(), request.getEntries(), request.isNotifyDevice())); } @Override @@ -318,57 +242,61 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer addWsCallback(deleteFuture, list -> onTimeSeriesDelete(tenantId, entityId, keys, list)); } - - @Override - public void saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, long value, FutureCallback callback) { - saveAndNotify(tenantId, entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new LongDataEntry(key, value) - , System.currentTimeMillis())), callback); - } - @Override - public void saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, String value, FutureCallback callback) { - saveAndNotify(tenantId, entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry(key, value) - , System.currentTimeMillis())), callback); - } - - @Override - public void saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, double value, FutureCallback callback) { - saveAndNotify(tenantId, entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new DoubleDataEntry(key, value) - , System.currentTimeMillis())), callback); - } - - @Override - public void saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, boolean value, FutureCallback callback) { - saveAndNotify(tenantId, entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new BooleanDataEntry(key, value) - , System.currentTimeMillis())), callback); - } - - @Override - public ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, long value) { + public ListenableFuture saveAttrAndNotify(AttributesSaveRequest request) { SettableFuture future = SettableFuture.create(); - saveAttrAndNotify(tenantId, entityId, scope, key, value, new VoidFutureCallback(future)); + request.setCallback(new VoidFutureCallback(future)); + save(request); return future; } - @Override - public ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, String value) { - SettableFuture future = SettableFuture.create(); - saveAttrAndNotify(tenantId, entityId, scope, key, value, new VoidFutureCallback(future)); - return future; - } + private void addEntityViewCallback(TenantId tenantId, EntityId entityId, List ts) { + if (EntityType.DEVICE.equals(entityId.getEntityType()) || EntityType.ASSET.equals(entityId.getEntityType())) { + Futures.addCallback(this.tbEntityViewService.findEntityViewsByTenantIdAndEntityIdAsync(tenantId, entityId), + new FutureCallback<>() { + @Override + public void onSuccess(@Nullable List result) { + if (result != null && !result.isEmpty()) { + Map> tsMap = new HashMap<>(); + for (TsKvEntry entry : ts) { + tsMap.computeIfAbsent(entry.getKey(), s -> new ArrayList<>()).add(entry); + } + for (EntityView entityView : result) { + List keys = entityView.getKeys() != null && entityView.getKeys().getTimeseries() != null ? + entityView.getKeys().getTimeseries() : new ArrayList<>(tsMap.keySet()); + List entityViewLatest = new ArrayList<>(); + long startTs = entityView.getStartTimeMs(); + long endTs = entityView.getEndTimeMs() == 0 ? Long.MAX_VALUE : entityView.getEndTimeMs(); + for (String key : keys) { + List entries = tsMap.get(key); + if (entries != null) { + Optional tsKvEntry = entries.stream() + .filter(entry -> entry.getTs() > startTs && entry.getTs() <= endTs) + .max(Comparator.comparingLong(TsKvEntry::getTs)); + tsKvEntry.ifPresent(entityViewLatest::add); + } + } + if (!entityViewLatest.isEmpty()) { + saveLatestAndNotify(tenantId, entityView.getId(), entityViewLatest, new FutureCallback<>() { + @Override + public void onSuccess(@Nullable Void tmp) { + } - @Override - public ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, double value) { - SettableFuture future = SettableFuture.create(); - saveAttrAndNotify(tenantId, entityId, scope, key, value, new VoidFutureCallback(future)); - return future; - } + @Override + public void onFailure(Throwable t) { + } + }); + } + } + } + } - @Override - public ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, boolean value) { - SettableFuture future = SettableFuture.create(); - saveAttrAndNotify(tenantId, entityId, scope, key, value, new VoidFutureCallback(future)); - return future; + @Override + public void onFailure(Throwable t) { + log.error("Error while finding entity views by tenantId and entityId", t); + } + }, MoreExecutors.directExecutor()); + } } private void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List attributes, boolean notifyDevice) { diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java index c861efb46f..20eff8b6b5 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java @@ -17,12 +17,12 @@ package org.thingsboard.server.service.telemetry; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import java.util.List; @@ -34,10 +34,7 @@ public interface InternalTelemetryService extends RuleEngineTelemetryService { ListenableFuture saveInternal(TimeseriesSaveRequest request); - @Deprecated(since = "3.7.0") - void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, String scope, List attributes, boolean notifyDevice, FutureCallback callback); - - void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributes, boolean notifyDevice, FutureCallback callback); + void saveInternal(AttributesSaveRequest request); void saveLatestAndNotifyInternal(TenantId tenantId, EntityId entityId, List ts, FutureCallback callback); diff --git a/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java b/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java index 40c7e435ca..05e7132ce8 100644 --- a/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java @@ -29,6 +29,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.TestPropertySource; import org.testcontainers.shaded.org.apache.commons.lang3.RandomStringUtils; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.alarm.Alarm; @@ -832,19 +833,25 @@ public class WebsocketApiTest extends AbstractControllerTest { private void sendAttributes(TenantId tenantId, EntityId entityId, TbAttributeSubscriptionScope scope, List attrData) throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); - tsService.saveAndNotify(tenantId, entityId, scope.getAttributeScope(), attrData, new FutureCallback() { - @Override - public void onSuccess(@Nullable Void result) { - log.debug("sendAttributes callback onSuccess"); - latch.countDown(); - } - - @Override - public void onFailure(Throwable t) { - log.error("Failed to sendAttributes", t); - latch.countDown(); - } - }); + tsService.save(AttributesSaveRequest.builder() + .tenantId(tenantId) + .entityId(entityId) + .scope(scope.getAttributeScope()) + .entries(attrData) + .callback(new FutureCallback<>() { + @Override + public void onSuccess(@Nullable Void result) { + log.debug("sendAttributes callback onSuccess"); + latch.countDown(); + } + + @Override + public void onFailure(Throwable t) { + log.error("Failed to sendAttributes", t); + latch.countDown(); + } + }) + .build()); assertThat(latch.await(TIMEOUT, TimeUnit.SECONDS)).as("await sendAttributes callback").isTrue(); } diff --git a/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java b/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java index 296191d61b..b7207da23a 100644 --- a/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java @@ -24,9 +24,11 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.test.util.ReflectionTestUtils; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.Device; @@ -72,7 +74,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; -import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.then; @@ -82,6 +84,7 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.thingsboard.server.service.state.DefaultDeviceStateService.ACTIVITY_STATE; @@ -208,9 +211,12 @@ public class DefaultDeviceStateServiceTest { service.onDeviceConnect(tenantId, deviceId, lastConnectTime); // THEN - then(telemetrySubscriptionService).should().saveAttrAndNotify( - eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(AttributeScope.SERVER_SCOPE), eq(LAST_CONNECT_TIME), eq(lastConnectTime), any() - ); + then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && + request.getScope().equals(AttributeScope.SERVER_SCOPE) && + request.getEntries().get(0).getKey().equals(LAST_CONNECT_TIME) && + request.getEntries().get(0).getValue().equals(lastConnectTime) + )); var msgCaptor = ArgumentCaptor.forClass(TbMsg.class); then(clusterService).should().pushMsgToRuleEngine(eq(tenantId), eq(deviceId), msgCaptor.capture(), any()); @@ -292,10 +298,12 @@ public class DefaultDeviceStateServiceTest { service.onDeviceDisconnect(tenantId, deviceId, lastDisconnectTime); // THEN - then(telemetrySubscriptionService).should().saveAttrAndNotify( - eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(AttributeScope.SERVER_SCOPE), - eq(LAST_DISCONNECT_TIME), eq(lastDisconnectTime), any() - ); + then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && + request.getScope().equals(AttributeScope.SERVER_SCOPE) && + request.getEntries().get(0).getKey().equals(LAST_DISCONNECT_TIME) && + request.getEntries().get(0).getValue().equals(lastDisconnectTime) + )); var msgCaptor = ArgumentCaptor.forClass(TbMsg.class); then(clusterService).should().pushMsgToRuleEngine(eq(tenantId), eq(deviceId), msgCaptor.capture(), any()); @@ -413,14 +421,18 @@ public class DefaultDeviceStateServiceTest { service.onDeviceInactivity(tenantId, deviceId, lastInactivityTime); // THEN - then(telemetrySubscriptionService).should().saveAttrAndNotify( - eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(AttributeScope.SERVER_SCOPE), - eq(INACTIVITY_ALARM_TIME), eq(lastInactivityTime), any() - ); - then(telemetrySubscriptionService).should().saveAttrAndNotify( - eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(AttributeScope.SERVER_SCOPE), - eq(ACTIVITY_STATE), eq(false), any() - ); + then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && + request.getScope().equals(AttributeScope.SERVER_SCOPE) && + request.getEntries().get(0).getKey().equals(INACTIVITY_ALARM_TIME) && + request.getEntries().get(0).getValue().equals(lastInactivityTime) + )); + then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && + request.getScope().equals(AttributeScope.SERVER_SCOPE) && + request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && + request.getEntries().get(0).getValue().equals(false) + )); var msgCaptor = ArgumentCaptor.forClass(TbMsg.class); then(clusterService).should() @@ -453,14 +465,17 @@ public class DefaultDeviceStateServiceTest { service.updateInactivityStateIfExpired(System.currentTimeMillis(), deviceId, deviceStateData); // THEN - then(telemetrySubscriptionService).should().saveAttrAndNotify( - eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(AttributeScope.SERVER_SCOPE), - eq(INACTIVITY_ALARM_TIME), anyLong(), any() - ); - then(telemetrySubscriptionService).should().saveAttrAndNotify( - eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(AttributeScope.SERVER_SCOPE), - eq(ACTIVITY_STATE), eq(false), any() - ); + then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && + request.getScope().equals(AttributeScope.SERVER_SCOPE) && + request.getEntries().get(0).getKey().equals(INACTIVITY_ALARM_TIME) + )); + then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && + request.getScope().equals(AttributeScope.SERVER_SCOPE) && + request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && + request.getEntries().get(0).getValue().equals(false) + )); var msgCaptor = ArgumentCaptor.forClass(TbMsg.class); then(clusterService).should() @@ -612,7 +627,9 @@ public class DefaultDeviceStateServiceTest { long newTimeout = System.currentTimeMillis() - deviceState.getLastActivityTime() + increase; service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, newTimeout); - verify(telemetrySubscriptionService, never()).saveAttrAndNotify(any(), eq(deviceId), any(AttributeScope.class), eq(ACTIVITY_STATE), any(), any()); + verify(telemetrySubscriptionService, never()).save(argThat((ArgumentMatcher) request -> + request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) + )); Thread.sleep(defaultTimeout + increase); service.checkStates(); activityVerify(false); @@ -651,7 +668,9 @@ public class DefaultDeviceStateServiceTest { long newTimeout = 1; Thread.sleep(newTimeout); - verify(telemetrySubscriptionService, never()).saveAttrAndNotify(any(), eq(deviceId), any(AttributeScope.class), eq(ACTIVITY_STATE), any(), any()); + verify(telemetrySubscriptionService, never()).save(argThat((ArgumentMatcher) request -> + request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) + )); } @Test @@ -672,8 +691,6 @@ public class DefaultDeviceStateServiceTest { service.onDeviceActivity(tenantId, deviceId, System.currentTimeMillis()); activityVerify(true); - verify(telemetrySubscriptionService, never()).saveAttrAndNotify(any(), eq(deviceId), any(AttributeScope.class), eq(ACTIVITY_STATE), any(), any()); - long newTimeout = 1; Thread.sleep(newTimeout); @@ -713,11 +730,17 @@ public class DefaultDeviceStateServiceTest { long newTimeout = 1; service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, newTimeout); - verify(telemetrySubscriptionService, never()).saveAttrAndNotify(any(), eq(deviceId), any(AttributeScope.class), eq(ACTIVITY_STATE), any(), any()); + verify(telemetrySubscriptionService, never()).save(argThat((ArgumentMatcher) request -> + request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) + )); } private void activityVerify(boolean isActive) { - verify(telemetrySubscriptionService).saveAttrAndNotify(any(), eq(deviceId), any(AttributeScope.class), eq(ACTIVITY_STATE), eq(isActive), any()); + verify(telemetrySubscriptionService).save(argThat((ArgumentMatcher) request -> + request.getEntityId().equals(deviceId) && + request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && + request.getEntries().get(0).getValue().equals(isActive) + )); } @Test @@ -763,21 +786,27 @@ public class DefaultDeviceStateServiceTest { // THEN assertThat(deviceState.isActive()).isEqualTo(true); assertThat(deviceState.getLastActivityTime()).isEqualTo(lastReportedActivity); - then(telemetrySubscriptionService).should().saveAttrAndNotify( - any(), eq(deviceId), any(AttributeScope.class), eq(LAST_ACTIVITY_TIME), eq(lastReportedActivity), any() - ); + then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + request.getEntityId().equals(deviceId) && + request.getEntries().get(0).getKey().equals(LAST_ACTIVITY_TIME) && + request.getEntries().get(0).getValue().equals(lastReportedActivity) + )); assertThat(deviceState.getLastInactivityAlarmTime()).isEqualTo(expectedInactivityAlarmTime); if (shouldSetInactivityAlarmTimeToZero) { - then(telemetrySubscriptionService).should().saveAttrAndNotify( - any(), eq(deviceId), any(AttributeScope.class), eq(INACTIVITY_ALARM_TIME), eq(0L), any() - ); + then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + request.getEntityId().equals(deviceId) && + request.getEntries().get(0).getKey().equals(INACTIVITY_ALARM_TIME) && + request.getEntries().get(0).getValue().equals(0L) + )); } if (shouldUpdateActivityStateToActive) { - then(telemetrySubscriptionService).should().saveAttrAndNotify( - eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(AttributeScope.SERVER_SCOPE), eq(ACTIVITY_STATE), eq(true), any() - ); + then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + request.getEntityId().equals(deviceId) && + request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && + request.getEntries().get(0).getValue().equals(true) + )); var msgCaptor = ArgumentCaptor.forClass(TbMsg.class); then(clusterService).should().pushMsgToRuleEngine(eq(tenantId), eq(deviceId), msgCaptor.capture(), any()); @@ -796,28 +825,28 @@ public class DefaultDeviceStateServiceTest { private static Stream provideParametersForUpdateActivityState() { return Stream.of( - Arguments.of(true, 100, 120, 80, 80, false, false), + Arguments.of(true, 100, 120, 80, 80, false, false), - Arguments.of(true, 100, 120, 100, 100, false, false), + Arguments.of(true, 100, 120, 100, 100, false, false), Arguments.of(false, 100, 120, 110, 110, false, true), - Arguments.of(true, 100, 100, 80, 80, false, false), + Arguments.of(true, 100, 100, 80, 80, false, false), - Arguments.of(true, 100, 100, 100, 100, false, false), + Arguments.of(true, 100, 100, 100, 100, false, false), - Arguments.of(false, 100, 100, 110, 0, true, true), + Arguments.of(false, 100, 100, 110, 0, true, true), - Arguments.of(false, 100, 110, 110, 0, true, true), + Arguments.of(false, 100, 110, 110, 0, true, true), - Arguments.of(false, 100, 110, 120, 0, true, true), + Arguments.of(false, 100, 110, 120, 0, true, true), - Arguments.of(true, 0, 0, 0, 0, false, false), + Arguments.of(true, 0, 0, 0, 0, false, false), - Arguments.of(false, 0, 0, 0, 0, true, true) + Arguments.of(false, 0, 0, 0, 0, true, true) ); } @@ -857,9 +886,10 @@ public class DefaultDeviceStateServiceTest { assertThat(deviceState.getInactivityTimeout()).isEqualTo(newInactivityTimeout); assertThat(deviceState.isActive()).isEqualTo(expectedActivityState); if (activityState && !expectedActivityState) { - then(telemetrySubscriptionService).should().saveAttrAndNotify( - any(), eq(deviceId), any(AttributeScope.class), eq(ACTIVITY_STATE), eq(false), any() - ); + then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && + request.getEntries().get(0).getValue().equals(false) + )); } } @@ -954,9 +984,10 @@ public class DefaultDeviceStateServiceTest { assertThat(state.getLastInactivityAlarmTime()).isEqualTo(expectedLastInactivityAlarmTime); if (shouldUpdateActivityStateToInactive) { - then(telemetrySubscriptionService).should().saveAttrAndNotify( - eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(AttributeScope.SERVER_SCOPE), eq(ACTIVITY_STATE), eq(false), any() - ); + then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && + request.getEntries().get(0).getValue().equals(false) + )); var msgCaptor = ArgumentCaptor.forClass(TbMsg.class); then(clusterService).should().pushMsgToRuleEngine(eq(tenantId), eq(deviceId), msgCaptor.capture(), any()); @@ -971,72 +1002,74 @@ public class DefaultDeviceStateServiceTest { assertThat(actualNotification.getDeviceId()).isEqualTo(deviceId); assertThat(actualNotification.isActive()).isFalse(); - then(telemetrySubscriptionService).should().saveAttrAndNotify( - eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(AttributeScope.SERVER_SCOPE), - eq(INACTIVITY_ALARM_TIME), eq(expectedLastInactivityAlarmTime), any() - ); + then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && + request.getScope().equals(AttributeScope.SERVER_SCOPE) && + request.getEntries().get(0).getKey().equals(INACTIVITY_ALARM_TIME) && + request.getEntries().get(0).getValue().equals(expectedLastInactivityAlarmTime) + )); } } private static Stream provideParametersForUpdateInactivityStateIfExpired() { return Stream.of( - Arguments.of(false, 100, 70, 90, 70, 60, false, 90, false), + Arguments.of(false, 100, 70, 90, 70, 60, false, 90, false), - Arguments.of(false, 100, 40, 50, 70, 10, false, 50, false), + Arguments.of(false, 100, 40, 50, 70, 10, false, 50, false), - Arguments.of(false, 100, 25, 60, 75, 25, false, 60, false), + Arguments.of(false, 100, 25, 60, 75, 25, false, 60, false), - Arguments.of(false, 100, 60, 70, 10, 50, false, 70, false), + Arguments.of(false, 100, 60, 70, 10, 50, false, 70, false), - Arguments.of(false, 100, 10, 15, 90, 10, false, 15, false), + Arguments.of(false, 100, 10, 15, 90, 10, false, 15, false), - Arguments.of(false, 100, 0, 40, 75, 0, false, 40, false), + Arguments.of(false, 100, 0, 40, 75, 0, false, 40, false), - Arguments.of(true, 100, 90, 80, 80, 50, true, 80, false), + Arguments.of(true, 100, 90, 80, 80, 50, true, 80, false), - Arguments.of(true, 100, 95, 90, 10, 50, true, 90, false), + Arguments.of(true, 100, 95, 90, 10, 50, true, 90, false), - Arguments.of(true, 100, 10, 10, 90, 10, false, 100, true), + Arguments.of(true, 100, 10, 10, 90, 10, false, 100, true), - Arguments.of(true, 100, 10, 10, 90, 11, true, 10, false), + Arguments.of(true, 100, 10, 10, 90, 11, true, 10, false), - Arguments.of(true, 100, 15, 10, 85, 5, false, 100, true), + Arguments.of(true, 100, 15, 10, 85, 5, false, 100, true), - Arguments.of(true, 100, 15, 10, 75, 5, false, 100, true), + Arguments.of(true, 100, 15, 10, 75, 5, false, 100, true), - Arguments.of(true, 100, 95, 90, 5, 50, false, 100, true), + Arguments.of(true, 100, 95, 90, 5, 50, false, 100, true), - Arguments.of(true, 100, 0, 0, 101, 0, true, 0, false), + Arguments.of(true, 100, 0, 0, 101, 0, true, 0, false), - Arguments.of(true, 100, 0, 0, 100, 0, false, 100, true), + Arguments.of(true, 100, 0, 0, 100, 0, false, 100, true), - Arguments.of(true, 100, 0, 0, 99, 0, false, 100, true), + Arguments.of(true, 100, 0, 0, 99, 0, false, 100, true), - Arguments.of(true, 100, 0, 0, 120, 10, true, 0, false), + Arguments.of(true, 100, 0, 0, 120, 10, true, 0, false), - Arguments.of(true, 100, 50, 0, 100, 0, true, 0, false), + Arguments.of(true, 100, 50, 0, 100, 0, true, 0, false), - Arguments.of(true, 100, 10, 0, 91, 0, true, 0, false), + Arguments.of(true, 100, 10, 0, 91, 0, true, 0, false), - Arguments.of(true, 100, 90, 0, 10, 0, false, 100, true), + Arguments.of(true, 100, 90, 0, 10, 0, false, 100, true), - Arguments.of(true, 100, 100, 100, 1, 0, true, 100, false), + Arguments.of(true, 100, 100, 100, 1, 0, true, 100, false), - Arguments.of(true, 100, 100, 100, 100, 100, true, 100, false), + Arguments.of(true, 100, 100, 100, 100, 100, true, 100, false), - Arguments.of(false, 100, 59, 60, 30, 10, false, 60, false), + Arguments.of(false, 100, 59, 60, 30, 10, false, 60, false), - Arguments.of(true, 100, 60, 60, 30, 10, false, 100, true), + Arguments.of(true, 100, 60, 60, 30, 10, false, 100, true), - Arguments.of(true, 100, 61, 60, 30, 10, false, 100, true), + Arguments.of(true, 100, 61, 60, 30, 10, false, 100, true), - Arguments.of(true, 0, 0, 0, 1, 0, true, 0, false), + Arguments.of(true, 0, 0, 0, 1, 0, true, 0, false), - Arguments.of(true, 0, 0, 0, 0, 0, false, 0, true), + Arguments.of(true, 0, 0, 0, 0, 0, false, 0, true), - Arguments.of(true, 100, 90, 80, 20, 70, true, 80, false), + Arguments.of(true, 100, 90, 80, 20, 70, true, 80, false), - Arguments.of(true, 100, 80, 90, 30, 70, true, 90, false) + Arguments.of(true, 100, 80, 90, 30, 70, true, 90, false) ); } @@ -1100,7 +1133,10 @@ public class DefaultDeviceStateServiceTest { // THEN await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { assertThat(service.deviceStates.get(deviceId).getState().isActive()).isEqualTo(false); - then(telemetrySubscriptionService).should().saveAttrAndNotify(eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(AttributeScope.SERVER_SCOPE), eq(ACTIVITY_STATE), eq(false), any()); + then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && + request.getEntries().get(0).getValue().equals(false) + )); }); } @@ -1127,10 +1163,31 @@ public class DefaultDeviceStateServiceTest { service.onDeviceActivity(tenantId, deviceId, currentTime); // THEN + ArgumentCaptor attributeRequestCaptor = ArgumentCaptor.forClass(AttributesSaveRequest.class); + then(telemetrySubscriptionService).should(times(2)).save(attributeRequestCaptor.capture()); + await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { assertThat(service.deviceStates.get(deviceId).getState().isActive()).isEqualTo(true); - then(telemetrySubscriptionService).should().saveAttrAndNotify(eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(AttributeScope.SERVER_SCOPE), eq(LAST_ACTIVITY_TIME), eq(currentTime), any()); - then(telemetrySubscriptionService).should().saveAttrAndNotify(eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(AttributeScope.SERVER_SCOPE), eq(ACTIVITY_STATE), eq(true), any()); + + assertThat(attributeRequestCaptor.getAllValues()).hasSize(2) + .anySatisfy(request -> { + assertThat(request.getTenantId()).isEqualTo(TenantId.SYS_TENANT_ID); + assertThat(request.getEntityId()).isEqualTo(deviceId); + assertThat(request.getScope()).isEqualTo(AttributeScope.SERVER_SCOPE); + assertThat(request.getEntries()).singleElement().satisfies(attributeKvEntry -> { + assertThat(attributeKvEntry.getKey()).isEqualTo(LAST_ACTIVITY_TIME); + assertThat(attributeKvEntry.getLongValue()).hasValue(currentTime); + }); + }) + .anySatisfy(request -> { + assertThat(request.getTenantId()).isEqualTo(TenantId.SYS_TENANT_ID); + assertThat(request.getEntityId()).isEqualTo(deviceId); + assertThat(request.getScope()).isEqualTo(AttributeScope.SERVER_SCOPE); + assertThat(request.getEntries()).singleElement().satisfies(attributeKvEntry -> { + assertThat(attributeKvEntry.getKey()).isEqualTo(ACTIVITY_STATE); + assertThat(attributeKvEntry.getBooleanValue()).hasValue(true); + }); + }); }); } @@ -1174,7 +1231,10 @@ public class DefaultDeviceStateServiceTest { // THEN await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { assertThat(service.deviceStates.get(deviceId).getState().isActive()).isEqualTo(true); - then(telemetrySubscriptionService).should().saveAttrAndNotify(eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(AttributeScope.SERVER_SCOPE), eq(ACTIVITY_STATE), eq(true), any()); + then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && + request.getEntries().get(0).getValue().equals(true) + )); }); } diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/AttributesSaveRequest.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/AttributesSaveRequest.java new file mode 100644 index 0000000000..c1b12081f4 --- /dev/null +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/AttributesSaveRequest.java @@ -0,0 +1,115 @@ +/** + * 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. + */ +package org.thingsboard.rule.engine.api; + +import com.google.common.util.concurrent.FutureCallback; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.thingsboard.server.common.data.AttributeScope; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; +import org.thingsboard.server.common.data.kv.KvEntry; + +import java.util.List; + +@Getter +@ToString +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class AttributesSaveRequest { + + private final TenantId tenantId; + private final EntityId entityId; + private final AttributeScope scope; + private final List entries; // todo: rename to attributes? same with timeseries + private final boolean notifyDevice; + @Setter + private FutureCallback callback; + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private TenantId tenantId; + private EntityId entityId; + private AttributeScope scope; + private List entries; + private boolean notifyDevice = true; + private FutureCallback callback; + + Builder() {} + + public Builder tenantId(TenantId tenantId) { + this.tenantId = tenantId; + return this; + } + + public Builder entityId(EntityId entityId) { + this.entityId = entityId; + return this; + } + + public Builder scope(AttributeScope scope) { + this.scope = scope; + return this; + } + + @Deprecated + public Builder scope(String scope) { + try { + this.scope = AttributeScope.valueOf(scope); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Invalid attribute scope '" + scope + "'"); + } + return this; + } + + public Builder entries(List entries) { + this.entries = entries; + return this; + } + + public Builder entry(AttributeKvEntry entry) { + return entries(List.of(entry)); + } + + public Builder entry(KvEntry kvEntry) { + return entry(new BaseAttributeKvEntry(kvEntry, System.currentTimeMillis())); + } + + public Builder notifyDevice(boolean notifyDevice) { + this.notifyDevice = notifyDevice; + return this; + } + + public Builder callback(FutureCallback callback) { + this.callback = callback; + return this; + } + + public AttributesSaveRequest build() { + return new AttributesSaveRequest(tenantId, entityId, scope, entries, notifyDevice, callback); + } + + } + +} diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java index 9f9a928839..bdc966ad88 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java @@ -20,7 +20,6 @@ import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; import org.thingsboard.server.common.data.kv.TsKvEntry; @@ -36,33 +35,11 @@ public interface RuleEngineTelemetryService { ListenableFuture saveAndNotify(TimeseriesSaveRequest request); - @Deprecated(since = "3.7.0") - void saveAndNotify(TenantId tenantId, EntityId entityId, String scope, List attributes, FutureCallback callback); - - void saveAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributes, FutureCallback callback); - - @Deprecated(since = "3.7.0") - void saveAndNotify(TenantId tenantId, EntityId entityId, String scope, List attributes, boolean notifyDevice, FutureCallback callback); - - void saveAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributes, boolean notifyDevice, FutureCallback callback); + void save(AttributesSaveRequest request); void saveLatestAndNotify(TenantId tenantId, EntityId entityId, List ts, FutureCallback callback); - ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, long value); - - ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, String value); - - ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, double value); - - ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, boolean value); - - void saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, long value, FutureCallback callback); - - void saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, String value, FutureCallback callback); - - void saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, double value, FutureCallback callback); - - void saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, String key, boolean value, FutureCallback callback); + ListenableFuture saveAttrAndNotify(AttributesSaveRequest request); void deleteAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List keys, FutureCallback callback); diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java index bcf021d3ca..de19c2b88e 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java @@ -23,6 +23,8 @@ import lombok.Setter; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import java.util.List; @@ -77,8 +79,11 @@ public class TimeseriesSaveRequest { } public Builder entry(TsKvEntry entry) { - this.entries = List.of(entry); - return this; + return entries(List.of(entry)); + } + + public Builder entry(KvEntry kvEntry) { + return entry(new BasicTsKvEntry(System.currentTimeMillis(), kvEntry)); } public Builder ttl(long ttl) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java index 26bb6d7a4c..e18ced7ee0 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java @@ -23,6 +23,7 @@ import com.google.gson.JsonPrimitive; import jakarta.annotation.Nullable; import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.DonAsynchron; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.EmptyNodeConfiguration; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; @@ -112,8 +113,13 @@ public class TbCopyAttributesToEntityViewNode implements TbNode { Set attributes = JsonConverter.convertToAttributes(JsonParser.parseString(msg.getData())); List filteredAttributes = attributes.stream().filter(attr -> attributeContainsInEntityView(scope, attr.getKey(), entityView)).collect(Collectors.toList()); - ctx.getTelemetryService().saveAndNotify(ctx.getTenantId(), entityView.getId(), scope, filteredAttributes, - getFutureCallback(ctx, msg, entityView)); + ctx.getTelemetryService().save(AttributesSaveRequest.builder() + .tenantId(ctx.getTenantId()) + .entityId(entityView.getId()) + .scope(scope) + .entries(filteredAttributes) + .callback(getFutureCallback(ctx, msg, entityView)) + .build()); } } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java index 893b06bfec..6f61de3fd3 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java @@ -24,6 +24,7 @@ import net.objecthunter.exp4j.Expression; import net.objecthunter.exp4j.ExpressionBuilder; import org.springframework.util.ConcurrentReferenceHashMap; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; @@ -38,6 +39,7 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.DoubleDataEntry; import org.thingsboard.server.common.data.kv.KvEntry; +import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; @@ -150,15 +152,20 @@ public class TbMathNode implements TbNode { private ListenableFuture saveAttribute(TbContext ctx, TbMsg msg, double result, TbMathResult mathResultDef) { AttributeScope attributeScope = getAttributeScope(mathResultDef.getAttributeScope()); + KvEntry kvEntry; if (isIntegerResult(mathResultDef, config.getOperation())) { var value = toIntValue(result); - return ctx.getTelemetryService().saveAttrAndNotify( - ctx.getTenantId(), msg.getOriginator(), attributeScope, mathResultDef.getKey(), value); + kvEntry = new LongDataEntry(mathResultDef.getKey(), value); } else { var value = toDoubleValue(mathResultDef, result); - return ctx.getTelemetryService().saveAttrAndNotify( - ctx.getTenantId(), msg.getOriginator(), attributeScope, mathResultDef.getKey(), value); + kvEntry = new DoubleDataEntry(mathResultDef.getKey(), value); } + return ctx.getTelemetryService().saveAttrAndNotify(AttributesSaveRequest.builder() + .tenantId(ctx.getTenantId()) + .entityId(msg.getOriginator()) + .scope(attributeScope) + .entry(kvEntry) + .build()); } private boolean isIntegerResult(TbMathResult mathResultDef, TbRuleNodeMathFunctionType function) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java index 40b82ba150..2250e0bbdd 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java @@ -17,11 +17,13 @@ package org.thingsboard.rule.engine.telemetry; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import com.google.gson.JsonParser; import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.DonAsynchron; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; @@ -56,10 +58,10 @@ import static org.thingsboard.server.common.data.msg.TbMsgType.POST_ATTRIBUTES_R version = 2, nodeDescription = "Saves attributes data", nodeDetails = "Saves entity attributes based on configurable scope parameter. Expects messages with 'POST_ATTRIBUTES_REQUEST' message type. " + - "If upsert(update/insert) operation is completed successfully rule node will send the incoming message via Success chain, otherwise, Failure chain is used. " + - "Additionally if checkbox Send attributes updated notification is set to true, rule node will put the \"Attributes Updated\" " + - "event for SHARED_SCOPE and SERVER_SCOPE 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).", + "If upsert(update/insert) operation is completed successfully rule node will send the incoming message via Success chain, otherwise, Failure chain is used. " + + "Additionally if checkbox Send attributes updated notification is set to true, rule node will put the \"Attributes Updated\" " + + "event for SHARED_SCOPE and SERVER_SCOPE 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" @@ -114,16 +116,17 @@ public class TbMsgAttributesNode implements TbNode { ctx.tellSuccess(msg); return; } - ctx.getTelemetryService().saveAndNotify( - ctx.getTenantId(), - msg.getOriginator(), - scope, - attributes, - config.isNotifyDevice() || checkNotifyDeviceMdValue(msg.getMetaData().getValue(NOTIFY_DEVICE_METADATA_KEY)), - sendAttributesUpdateNotification ? - new AttributesUpdateNodeCallback(ctx, msg, scope.name(), attributes) : - new TelemetryNodeCallback(ctx, msg) - ); + FutureCallback callback = sendAttributesUpdateNotification ? + new AttributesUpdateNodeCallback(ctx, msg, scope.name(), attributes) : + new TelemetryNodeCallback(ctx, msg); + ctx.getTelemetryService().save(AttributesSaveRequest.builder() + .tenantId(ctx.getTenantId()) + .entityId(msg.getOriginator()) + .scope(scope) + .entries(attributes) + .notifyDevice(config.isNotifyDevice() || checkNotifyDeviceMdValue(msg.getMetaData().getValue(NOTIFY_DEVICE_METADATA_KEY))) + .callback(callback) + .build()); } List filterChangedAttr(List currentAttributes, List newAttributes) { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNodeTest.java index 4166c05a7a..fcd6083eb2 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNodeTest.java @@ -24,8 +24,10 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.mockito.ThrowingConsumer; import org.mockito.junit.jupiter.MockitoExtension; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.EmptyNodeConfiguration; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.TbContext; @@ -37,7 +39,6 @@ import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.objects.AttributesEntityView; @@ -56,6 +57,7 @@ import java.util.UUID; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.assertArg; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.verify; @@ -113,10 +115,10 @@ public class TbCopyAttributesToEntityViewNodeTest { mockEntityViewLookup(entityView); when(ctxMock.getTelemetryService()).thenReturn(telemetryServiceMock); doAnswer(invocation -> { - FutureCallback callback = invocation.getArgument(4); - callback.onSuccess(null); + AttributesSaveRequest request = invocation.getArgument(0); + request.getCallback().onSuccess(null); return null; - }).when(telemetryServiceMock).saveAndNotify(any(), any(), any(AttributeScope.class), anyList(), any(FutureCallback.class)); + }).when(telemetryServiceMock).save(any(AttributesSaveRequest.class)); TbMsg newMsg = TbMsg.newMsg(msg, msg.getQueueName(), msg.getRuleChainId(), msg.getRuleNodeId()); // TODO: use newMsg() with any(TbMsgType.class), replace in other tests as well. doAnswer(invocation -> newMsg).when(ctxMock).newMsg(any(), any(String.class), any(), any(), any(), any()); @@ -124,13 +126,15 @@ public class TbCopyAttributesToEntityViewNodeTest { node.onMsg(ctxMock, msg); verify(entityViewServiceMock).findEntityViewsByTenantIdAndEntityIdAsync(eq(TENANT_ID), eq(DEVICE_ID)); - ArgumentCaptor> filteredAttributesCaptor = ArgumentCaptor.forClass(List.class); - verify(telemetryServiceMock).saveAndNotify(eq(TENANT_ID), eq(ENTITY_VIEW_ID), eq(AttributeScope.CLIENT_SCOPE), - filteredAttributesCaptor.capture(), any(FutureCallback.class)); - List filteredAttributesCaptorValue = filteredAttributesCaptor.getValue(); - assertThat(filteredAttributesCaptorValue.size()).isEqualTo(1); - assertThat(filteredAttributesCaptorValue.get(0).getKey()).isEqualTo("clientAttribute1"); - assertThat(filteredAttributesCaptorValue.get(0).getValue()).isEqualTo(100L); + verify(telemetryServiceMock).save(assertArg((ThrowingConsumer) request -> { + assertThat(request.getTenantId()).isEqualTo(TENANT_ID); + assertThat(request.getEntityId()).isEqualTo(ENTITY_VIEW_ID); + assertThat(request.getScope()).isEqualTo(AttributeScope.CLIENT_SCOPE); + + assertThat(request.getEntries().size()).isEqualTo(1); + assertThat(request.getEntries().get(0).getKey()).isEqualTo("clientAttribute1"); + assertThat(request.getEntries().get(0).getValue()).isEqualTo(100L); + })); verify(ctxMock).ack(eq(msg)); verify(ctxMock).enqueueForTellNext(eq(newMsg), eq(TbNodeConnectionType.SUCCESS)); verifyNoMoreInteractions(ctxMock, entityViewServiceMock, telemetryServiceMock); @@ -195,17 +199,22 @@ public class TbCopyAttributesToEntityViewNodeTest { mockEntityViewLookup(entityView); when(ctxMock.getTelemetryService()).thenReturn(telemetryServiceMock); doAnswer(invocation -> { - FutureCallback callback = invocation.getArgument(4); - callback.onSuccess(null); + AttributesSaveRequest request = invocation.getArgument(0); + request.getCallback().onSuccess(null); return null; - }).when(telemetryServiceMock).saveAndNotify(any(), any(), any(AttributeScope.class), anyList(), any(FutureCallback.class)); + }).when(telemetryServiceMock).save(any(AttributesSaveRequest.class)); TbMsg newMsg = TbMsg.newMsg(msg, msg.getQueueName(), msg.getRuleChainId(), msg.getRuleNodeId()); doAnswer(invocation -> newMsg).when(ctxMock).newMsg(any(), any(String.class), any(), any(), any(), any()); node.onMsg(ctxMock, msg); verify(entityViewServiceMock).findEntityViewsByTenantIdAndEntityIdAsync(eq(TENANT_ID), eq(DEVICE_ID)); - verify(telemetryServiceMock).saveAndNotify(eq(TENANT_ID), eq(ENTITY_VIEW_ID), eq(AttributeScope.CLIENT_SCOPE), eq(Collections.emptyList()), any(FutureCallback.class)); + verify(telemetryServiceMock).save(assertArg((ThrowingConsumer) request -> { + assertThat(request.getTenantId()).isEqualTo(TENANT_ID); + assertThat(request.getEntityId()).isEqualTo(ENTITY_VIEW_ID); + assertThat(request.getScope()).isEqualTo(AttributeScope.CLIENT_SCOPE); + assertThat(request.getEntries().isEmpty()).isTrue(); + })); verify(ctxMock).ack(eq(msg)); verify(ctxMock).enqueueForTellNext(eq(newMsg), eq(TbNodeConnectionType.SUCCESS)); verifyNoMoreInteractions(ctxMock, entityViewServiceMock, telemetryServiceMock); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java index 137a51d394..9104236be0 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java @@ -46,6 +46,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.DoubleDataEntry; +import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.TbMsg; @@ -69,8 +70,6 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyDouble; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.assertArg; import static org.mockito.ArgumentMatchers.eq; @@ -435,14 +434,16 @@ public class TbMathNodeTest { TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 5).toString()); - when(telemetryService.saveAttrAndNotify(any(), any(), any(AttributeScope.class), anyString(), anyDouble())) + when(telemetryService.saveAttrAndNotify(any())) .thenReturn(Futures.immediateFuture(null)); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); - verify(telemetryService, times(1)).saveAttrAndNotify(any(), any(), any(AttributeScope.class), anyString(), anyDouble()); + verify(telemetryService, times(1)).saveAttrAndNotify(assertArg(request -> { + assertThat(request.getEntries()).singleElement().extracting(KvEntry::getValue).isInstanceOf(Double.class); + })); TbMsg resultMsg = msgCaptor.getValue(); assertNotNull(resultMsg); @@ -554,7 +555,7 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); node.onMsg(ctx, msg); ArgumentCaptor tCaptor = ArgumentCaptor.forClass(Throwable.class); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java index b5cf5533b8..74aec7b566 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java @@ -22,9 +22,10 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.mockito.ArgumentCaptor; +import org.mockito.ThrowingConsumer; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.AbstractRuleNodeUpgradeTest; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; @@ -53,6 +54,7 @@ import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.assertArg; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.willCallRealMethod; import static org.mockito.Mockito.mock; @@ -169,17 +171,15 @@ class TbMsgAttributesNodeTest extends AbstractRuleNodeUpgradeTest { node.saveAttr(testAttrList, ctxMock, testTbMsg, AttributeScope.SHARED_SCOPE, false); - ArgumentCaptor notifyDeviceCaptor = ArgumentCaptor.forClass(Boolean.class); - - verify(telemetryServiceMock, times(1)).saveAndNotify( - eq(tenantId), eq(deviceId), eq(AttributeScope.SHARED_SCOPE), - eq(testAttrList), notifyDeviceCaptor.capture(), any() - ); - boolean notifyDevice = notifyDeviceCaptor.getValue(); - assertThat(notifyDevice).isEqualTo(expectedArgumentValue); + verify(telemetryServiceMock, times(1)).save(assertArg((ThrowingConsumer) request -> { + assertThat(request.getTenantId()).isEqualTo(tenantId); + assertThat(request.getEntityId()).isEqualTo(deviceId); + assertThat(request.getScope()).isEqualTo(AttributeScope.SHARED_SCOPE); + assertThat(request.getEntries()).isEqualTo(testAttrList); + assertThat(request.isNotifyDevice()).isEqualTo(expectedArgumentValue); + })); } - // Rule nodes upgrade private static Stream givenFromVersionAndConfig_whenUpgrade_thenVerifyHasChangesAndConfig() { return Stream.of( diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java index d80a0bd340..a66c27e06a 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java @@ -25,6 +25,7 @@ import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.MethodSource; import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.mockito.ThrowingConsumer; import org.mockito.junit.jupiter.MockitoExtension; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; @@ -129,12 +130,12 @@ public class TbMsgTimeseriesNodeTest { TimeseriesSaveRequest request = invocation.getArgument(0); request.getCallback().onSuccess(null); return null; - }).when(telemetryServiceMock).save(any()); + }).when(telemetryServiceMock).save(any(TimeseriesSaveRequest.class)); node.onMsg(ctxMock, msg); List expectedList = getTsKvEntriesListWithTs(data, System.currentTimeMillis()); - verify(telemetryServiceMock).save(assertArg(request -> { + verify(telemetryServiceMock).save(assertArg((ThrowingConsumer) request -> { assertThat(request.getTenantId()).isEqualTo(TENANT_ID); assertThat(request.getCustomerId()).isNull(); assertThat(request.getEntityId()).isEqualTo(DEVICE_ID); @@ -170,12 +171,12 @@ public class TbMsgTimeseriesNodeTest { TimeseriesSaveRequest request = invocation.getArgument(0); request.getCallback().onSuccess(null); return null; - }).when(telemetryServiceMock).save(any()); + }).when(telemetryServiceMock).save(any(TimeseriesSaveRequest.class)); node.onMsg(ctxMock, msg); List expectedList = getTsKvEntriesListWithTs(data, ts); - verify(telemetryServiceMock).save(assertArg(request -> { + verify(telemetryServiceMock).save(assertArg((ThrowingConsumer) request -> { assertThat(request.getTenantId()).isEqualTo(TENANT_ID); assertThat(request.getCustomerId()).isNull(); assertThat(request.getEntityId()).isEqualTo(DEVICE_ID); @@ -208,7 +209,7 @@ public class TbMsgTimeseriesNodeTest { TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metadata, data); node.onMsg(ctxMock, msg); - verify(telemetryServiceMock).save(assertArg(request -> { + verify(telemetryServiceMock).save(assertArg((ThrowingConsumer) request -> { assertThat(request.getTenantId()).isEqualTo(TENANT_ID); assertThat(request.getCustomerId()).isNull(); assertThat(request.getEntityId()).isEqualTo(DEVICE_ID); From 3db304e02111c6cf67095b9c5bfddcdb72fd3f80 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 17 Dec 2024 16:11:34 +0200 Subject: [PATCH 015/108] Refactor saveAndNotify with returned future --- .../DefaultTelemetrySubscriptionService.java | 36 ------------------- .../engine/api/AttributesSaveRequest.java | 21 ++++++++--- .../api/RuleEngineTelemetryService.java | 5 --- .../engine/api/TimeseriesSaveRequest.java | 19 ++++++++-- .../rule/engine/math/TbMathNode.java | 11 ++++-- .../rule/engine/math/TbMathNodeTest.java | 30 +++++++++++----- 6 files changed, 64 insertions(+), 58 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java index bc78139d2f..793aabc6cc 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java @@ -19,7 +19,6 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; -import com.google.common.util.concurrent.SettableFuture; import jakarta.annotation.Nullable; import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; @@ -127,14 +126,6 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer } } - @Override - public ListenableFuture saveAndNotify(TimeseriesSaveRequest request) { - SettableFuture future = SettableFuture.create(); - request.setCallback(new VoidFutureCallback(future)); - save(request); - return future; - } - @Override public ListenableFuture saveInternal(TimeseriesSaveRequest request) { TenantId tenantId = request.getTenantId(); @@ -242,14 +233,6 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer addWsCallback(deleteFuture, list -> onTimeSeriesDelete(tenantId, entityId, keys, list)); } - @Override - public ListenableFuture saveAttrAndNotify(AttributesSaveRequest request) { - SettableFuture future = SettableFuture.create(); - request.setCallback(new VoidFutureCallback(future)); - save(request); - return future; - } - private void addEntityViewCallback(TenantId tenantId, EntityId entityId, List ts) { if (EntityType.DEVICE.equals(entityId.getEntityType()) || EntityType.ASSET.equals(entityId.getEntityType())) { Futures.addCallback(this.tbEntityViewService.findEntityViewsByTenantIdAndEntityIdAsync(tenantId, entityId), @@ -397,23 +380,4 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer }; } - private static class VoidFutureCallback implements FutureCallback { - private final SettableFuture future; - - public VoidFutureCallback(SettableFuture future) { - this.future = future; - } - - @Override - public void onSuccess(Void result) { - future.set(null); - } - - @Override - public void onFailure(Throwable t) { - future.setException(t); - } - - } - } diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/AttributesSaveRequest.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/AttributesSaveRequest.java index c1b12081f4..22fa8de6de 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/AttributesSaveRequest.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/AttributesSaveRequest.java @@ -16,10 +16,10 @@ package org.thingsboard.rule.engine.api; import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.SettableFuture; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; -import lombok.Setter; import lombok.ToString; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.id.EntityId; @@ -38,10 +38,9 @@ public class AttributesSaveRequest { private final TenantId tenantId; private final EntityId entityId; private final AttributeScope scope; - private final List entries; // todo: rename to attributes? same with timeseries + private final List entries; private final boolean notifyDevice; - @Setter - private FutureCallback callback; + private final FutureCallback callback; public static Builder builder() { return new Builder(); @@ -106,6 +105,20 @@ public class AttributesSaveRequest { return this; } + public Builder future(SettableFuture future) { + return callback(new FutureCallback<>() { + @Override + public void onSuccess(Void result) { + future.set(result); + } + + @Override + public void onFailure(Throwable t) { + future.setException(t); + } + }); + } + public AttributesSaveRequest build() { return new AttributesSaveRequest(tenantId, entityId, scope, entries, notifyDevice, callback); } diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java index bdc966ad88..420c0c659b 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java @@ -16,7 +16,6 @@ package org.thingsboard.rule.engine.api; import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -33,14 +32,10 @@ public interface RuleEngineTelemetryService { void save(TimeseriesSaveRequest request); - ListenableFuture saveAndNotify(TimeseriesSaveRequest request); - void save(AttributesSaveRequest request); void saveLatestAndNotify(TenantId tenantId, EntityId entityId, List ts, FutureCallback callback); - ListenableFuture saveAttrAndNotify(AttributesSaveRequest request); - void deleteAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List keys, FutureCallback callback); void deleteAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List keys, boolean notifyDevice, FutureCallback callback); diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java index de19c2b88e..57fba9232f 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java @@ -16,10 +16,10 @@ package org.thingsboard.rule.engine.api; import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.SettableFuture; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; -import lombok.Setter; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -39,8 +39,7 @@ public class TimeseriesSaveRequest { private final List entries; private final long ttl; private final boolean saveLatest; - @Setter - private FutureCallback callback; + private final FutureCallback callback; public static Builder builder() { return new Builder(); @@ -101,6 +100,20 @@ public class TimeseriesSaveRequest { return this; } + public Builder future(SettableFuture future) { + return callback(new FutureCallback<>() { + @Override + public void onSuccess(Void result) { + future.set(result); + } + + @Override + public void onFailure(Throwable t) { + future.setException(t); + } + }); + } + public TimeseriesSaveRequest build() { return new TimeseriesSaveRequest(tenantId, customerId, entityId, entries, ttl, saveLatest, callback); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java index 6f61de3fd3..1d18290129 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; +import com.google.common.util.concurrent.SettableFuture; import lombok.extern.slf4j.Slf4j; import net.objecthunter.exp4j.Expression; import net.objecthunter.exp4j.ExpressionBuilder; @@ -143,11 +144,14 @@ public class TbMathNode implements TbNode { private ListenableFuture saveTimeSeries(TbContext ctx, TbMsg msg, double result, TbMathResult mathResultDef) { final BasicTsKvEntry basicTsKvEntry = new BasicTsKvEntry(System.currentTimeMillis(), new DoubleDataEntry(mathResultDef.getKey(), result)); - return ctx.getTelemetryService().saveAndNotify(TimeseriesSaveRequest.builder() + SettableFuture future = SettableFuture.create(); + ctx.getTelemetryService().save(TimeseriesSaveRequest.builder() .tenantId(ctx.getTenantId()) .entityId(msg.getOriginator()) .entry(basicTsKvEntry) + .future(future) .build()); + return future; } private ListenableFuture saveAttribute(TbContext ctx, TbMsg msg, double result, TbMathResult mathResultDef) { @@ -160,12 +164,15 @@ public class TbMathNode implements TbNode { var value = toDoubleValue(mathResultDef, result); kvEntry = new DoubleDataEntry(mathResultDef.getKey(), value); } - return ctx.getTelemetryService().saveAttrAndNotify(AttributesSaveRequest.builder() + SettableFuture future = SettableFuture.create(); + ctx.getTelemetryService().save(AttributesSaveRequest.builder() .tenantId(ctx.getTenantId()) .entityId(msg.getOriginator()) .scope(attributeScope) .entry(kvEntry) + .future(future) .build()); + return future; } private boolean isIntegerResult(TbMathResult mathResultDef, TbRuleNodeMathFunctionType function) { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java index 9104236be0..bec8946277 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java @@ -30,14 +30,17 @@ import org.junit.jupiter.params.provider.MethodSource; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; +import org.mockito.ThrowingConsumer; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.verification.Timeout; import org.thingsboard.common.util.AbstractListeningExecutor; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.id.DeviceId; @@ -76,6 +79,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.willAnswer; import static org.mockito.BDDMockito.willReturn; import static org.mockito.BDDMockito.willThrow; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -433,15 +437,17 @@ public class TbMathNodeTest { ); TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 5).toString()); - - when(telemetryService.saveAttrAndNotify(any())) - .thenReturn(Futures.immediateFuture(null)); + doAnswer(invocation -> { + AttributesSaveRequest request = invocation.getArgument(0); + request.getCallback().onSuccess(null); + return null; + }).when(telemetryService).save(any(AttributesSaveRequest.class)); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); - verify(telemetryService, times(1)).saveAttrAndNotify(assertArg(request -> { + verify(telemetryService, times(1)).save(assertArg((ThrowingConsumer) request -> { assertThat(request.getEntries()).singleElement().extracting(KvEntry::getValue).isInstanceOf(Double.class); })); @@ -461,13 +467,17 @@ public class TbMathNodeTest { ); TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 5).toString()); - when(telemetryService.saveAndNotify(any())).thenReturn(Futures.immediateFuture(null)); + doAnswer(invocation -> { + TimeseriesSaveRequest request = invocation.getArgument(0); + request.getCallback().onSuccess(null); + return null; + }).when(telemetryService).save(any(TimeseriesSaveRequest.class)); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); - verify(telemetryService, times(1)).saveAndNotify(assertArg(request -> { + verify(telemetryService, times(1)).save(assertArg((ThrowingConsumer) request -> { assertThat(request.getEntries()).size().isOne(); assertThat(request.isSaveLatest()).isTrue(); })); @@ -488,13 +498,17 @@ public class TbMathNodeTest { ); TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 5).toString()); - when(telemetryService.saveAndNotify(any())).thenReturn(Futures.immediateFuture(null)); + doAnswer(invocation -> { + TimeseriesSaveRequest request = invocation.getArgument(0); + request.getCallback().onSuccess(null); + return null; + }).when(telemetryService).save(any(TimeseriesSaveRequest.class)); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); - verify(telemetryService, times(1)).saveAndNotify(assertArg(request -> { + verify(telemetryService, times(1)).save(assertArg((ThrowingConsumer) request -> { assertThat(request.getEntries()).size().isOne(); assertThat(request.isSaveLatest()).isTrue(); })); From eb7bc8695ba00451db345288f1d5ccc2019ec908 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 17 Dec 2024 17:27:41 +0200 Subject: [PATCH 016/108] Refactor deleteAndNotify and deleteAndNotifyInternal for attributes --- .../controller/TelemetryController.java | 45 ++++--- .../DefaultTbApiUsageStateService.java | 8 +- .../device/ClaimDevicesServiceImpl.java | 22 ++-- .../service/edge/rpc/EdgeGrpcService.java | 8 +- .../telemetry/BaseTelemetryProcessor.java | 2 +- .../DefaultTbEntityViewService.java | 77 +++++++----- .../ota/DefaultOtaPackageStateService.java | 18 ++- .../state/DefaultDeviceStateService.java | 4 +- .../DefaultRuleEngineStatisticsService.java | 4 +- .../csv/AbstractBulkImportService.java | 4 +- .../impl/BaseEntityImportService.java | 2 +- .../system/DefaultSystemInfoService.java | 2 +- .../DefaultTelemetrySubscriptionService.java | 114 +++++++---------- .../telemetry/InternalTelemetryService.java | 14 +-- .../server/controller/WebsocketApiTest.java | 4 +- .../state/DefaultDeviceStateServiceTest.java | 39 +++--- .../dao/attributes/AttributesService.java | 6 - .../dao/attributes/BaseAttributesService.java | 14 --- .../attributes/CachedAttributesService.java | 10 -- .../engine/api/AttributesDeleteRequest.java | 117 ++++++++++++++++++ .../api/RuleEngineTelemetryService.java | 12 +- .../engine/api/TimeseriesSaveRequest.java | 10 +- .../TbCopyAttributesToEntityViewNode.java | 12 +- .../rule/engine/math/TbMathNode.java | 4 +- .../engine/telemetry/TbMsgAttributesNode.java | 2 +- .../telemetry/TbMsgDeleteAttributesNode.java | 19 +-- .../engine/telemetry/TbMsgTimeseriesNode.java | 2 +- .../TbCopyAttributesToEntityViewNodeTest.java | 30 ++--- .../rule/engine/math/TbMathNodeTest.java | 12 +- .../telemetry/TbMsgAttributesNodeTest.java | 4 +- .../TbMsgDeleteAttributesNodeTest.java | 16 +-- .../telemetry/TbMsgTimeseriesNodeTest.java | 10 +- 32 files changed, 364 insertions(+), 283 deletions(-) create mode 100644 rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/AttributesDeleteRequest.java diff --git a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java index b6d854399c..a518eaa1af 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java @@ -47,6 +47,7 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.request.async.DeferredResult; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.rule.engine.api.AttributesDeleteRequest; import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.adaptor.JsonConverter; @@ -589,24 +590,30 @@ public class TelemetryController extends BaseController { SecurityUser user = getCurrentUser(); return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_ATTRIBUTES, entityIdSrc, (result, tenantId, entityId) -> { - tsSubService.deleteAndNotify(tenantId, entityId, scope, keys, new FutureCallback() { - @Override - public void onSuccess(@Nullable Void tmp) { - logAttributesDeleted(user, entityId, scope, keys, null); - if (entityIdSrc.getEntityType().equals(EntityType.DEVICE)) { - DeviceId deviceId = new DeviceId(entityId.getId()); - tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete( - user.getTenantId(), deviceId, scope.name(), keys), null); - } - result.setResult(new ResponseEntity<>(HttpStatus.OK)); - } + tsSubService.deleteAttributes(AttributesDeleteRequest.builder() + .tenantId(tenantId) + .entityId(entityId) + .scope(scope) + .keys(keys) + .callback(new FutureCallback<>() { + @Override + public void onSuccess(@Nullable Void tmp) { + logAttributesDeleted(user, entityId, scope, keys, null); + if (entityIdSrc.getEntityType().equals(EntityType.DEVICE)) { + DeviceId deviceId = new DeviceId(entityId.getId()); + tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete( + user.getTenantId(), deviceId, scope.name(), keys), null); + } + result.setResult(new ResponseEntity<>(HttpStatus.OK)); + } - @Override - public void onFailure(Throwable t) { - logAttributesDeleted(user, entityId, scope, keys, t); - result.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); - } - }); + @Override + public void onFailure(Throwable t) { + logAttributesDeleted(user, entityId, scope, keys, t); + result.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); + } + }) + .build()); }); } @@ -626,7 +633,7 @@ public class TelemetryController extends BaseController { } SecurityUser user = getCurrentUser(); return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_ATTRIBUTES, entityIdSrc, (result, tenantId, entityId) -> { - tsSubService.save(AttributesSaveRequest.builder() + tsSubService.saveAttributes(AttributesSaveRequest.builder() .tenantId(tenantId) .entityId(entityId) .scope(scope) @@ -680,7 +687,7 @@ public class TelemetryController extends BaseController { TenantProfile tenantProfile = tenantProfileCache.get(tenantId); tenantTtl = TimeUnit.DAYS.toSeconds(((DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration()).getDefaultStorageTtlDays()); } - tsSubService.save(TimeseriesSaveRequest.builder() + tsSubService.saveTimeseries(TimeseriesSaveRequest.builder() .tenantId(tenantId) .customerId(user.getCustomerId()) .entityId(entityId) diff --git a/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java b/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java index d9c5077ab7..3a351528e4 100644 --- a/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java @@ -215,7 +215,7 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService updateLock.unlock(); } log.trace("[{}][{}] Saving new stats: {}", tenantId, ownerId, updatedEntries); - tsWsService.saveInternal(TimeseriesSaveRequest.builder() + tsWsService.saveTimeseriesInternal(TimeseriesSaveRequest.builder() .tenantId(tenantId) .entityId(usageState.getApiUsageState().getId()) .entries(updatedEntries) @@ -327,7 +327,7 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService } } if (!profileThresholds.isEmpty()) { - tsWsService.saveInternal(TimeseriesSaveRequest.builder() + tsWsService.saveTimeseriesInternal(TimeseriesSaveRequest.builder() .tenantId(tenantId) .entityId(id) .entries(profileThresholds) @@ -359,7 +359,7 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService long ts = System.currentTimeMillis(); List stateTelemetry = new ArrayList<>(); result.forEach((apiFeature, aState) -> stateTelemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(apiFeature.getApiStateKey(), aState.name())))); - tsWsService.saveInternal(TimeseriesSaveRequest.builder() + tsWsService.saveTimeseriesInternal(TimeseriesSaveRequest.builder() .tenantId(state.getTenantId()) .entityId(state.getApiUsageState().getId()) .entries(stateTelemetry) @@ -452,7 +452,7 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService .map(key -> new BasicTsKvEntry(state.getCurrentCycleTs(), new LongDataEntry(key.getApiCountKey(), 0L))) .collect(Collectors.toList()); - tsWsService.saveInternal(TimeseriesSaveRequest.builder() + tsWsService.saveTimeseriesInternal(TimeseriesSaveRequest.builder() .tenantId(state.getTenantId()) .entityId(state.getApiUsageState().getId()) .entries(counts) diff --git a/application/src/main/java/org/thingsboard/server/service/device/ClaimDevicesServiceImpl.java b/application/src/main/java/org/thingsboard/server/service/device/ClaimDevicesServiceImpl.java index cd3e096ad8..f82f4c74d5 100644 --- a/application/src/main/java/org/thingsboard/server/service/device/ClaimDevicesServiceImpl.java +++ b/application/src/main/java/org/thingsboard/server/service/device/ClaimDevicesServiceImpl.java @@ -28,6 +28,7 @@ import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.AttributesDeleteRequest; import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.server.common.data.AttributeScope; @@ -178,7 +179,7 @@ public class ClaimDevicesServiceImpl implements ClaimDevicesService { return Futures.immediateFuture(new ReclaimResult(unassignedCustomer)); } SettableFuture result = SettableFuture.create(); - telemetryService.save(AttributesSaveRequest.builder() + telemetryService.saveAttributes(AttributesSaveRequest.builder() .tenantId(tenantId) .entityId(savedDevice.getId()) .scope(AttributeScope.SERVER_SCOPE) @@ -223,18 +224,13 @@ public class ClaimDevicesServiceImpl implements ClaimDevicesService { cache.evict(data.getKey()); } SettableFuture result = SettableFuture.create(); - telemetryService.deleteAndNotify(device.getTenantId(), - device.getId(), AttributeScope.SERVER_SCOPE, Arrays.asList(CLAIM_ATTRIBUTE_NAME, CLAIM_DATA_ATTRIBUTE_NAME), new FutureCallback<>() { - @Override - public void onSuccess(@Nullable Void tmp) { - result.set(tmp); - } - - @Override - public void onFailure(Throwable t) { - result.setException(t); - } - }); + telemetryService.deleteAttributes(AttributesDeleteRequest.builder() + .tenantId(device.getTenantId()) + .entityId(device.getId()) + .scope(AttributeScope.SERVER_SCOPE) + .keys(Arrays.asList(CLAIM_ATTRIBUTE_NAME, CLAIM_DATA_ATTRIBUTE_NAME)) + .future(result) + .build()); return result; } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java index 7551302d33..75cebfa394 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java @@ -503,14 +503,14 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i private void save(TenantId tenantId, EdgeId edgeId, String key, long value) { log.debug("[{}][{}] Updating long edge telemetry [{}] [{}]", tenantId, edgeId, key, value); if (persistToTelemetry) { - tsSubService.save(TimeseriesSaveRequest.builder() + tsSubService.saveTimeseries(TimeseriesSaveRequest.builder() .tenantId(tenantId) .entityId(edgeId) .entry(new LongDataEntry(key, value)) .callback(new AttributeSaveCallback(tenantId, edgeId, key, value)) .build()); } else { - tsSubService.save(AttributesSaveRequest.builder() + tsSubService.saveAttributes(AttributesSaveRequest.builder() .tenantId(tenantId) .entityId(edgeId) .scope(AttributeScope.SERVER_SCOPE) @@ -523,14 +523,14 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i private void save(TenantId tenantId, EdgeId edgeId, String key, boolean value) { log.debug("[{}][{}] Updating boolean edge telemetry [{}] [{}]", tenantId, edgeId, key, value); if (persistToTelemetry) { - tsSubService.save(TimeseriesSaveRequest.builder() + tsSubService.saveTimeseries(TimeseriesSaveRequest.builder() .tenantId(tenantId) .entityId(edgeId) .entry(new BooleanDataEntry(key, value)) .callback(new AttributeSaveCallback(tenantId, edgeId, key, value)) .build()); } else { - tsSubService.save(AttributesSaveRequest.builder() + tsSubService.saveAttributes(AttributesSaveRequest.builder() .tenantId(tenantId) .entityId(edgeId) .scope(AttributeScope.SERVER_SCOPE) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java index 9ef40280c9..bc3f660cbf 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java @@ -278,7 +278,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { JsonObject json = JsonUtils.getJsonObject(msg.getKvList()); List attributes = new ArrayList<>(JsonConverter.convertToAttributes(json)); String scope = metaData.getValue("scope"); - tsSubService.save(AttributesSaveRequest.builder() + tsSubService.saveAttributes(AttributesSaveRequest.builder() .tenantId(tenantId) .entityId(entityId) .scope(AttributeScope.valueOf(scope)) diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java index 790b1086b4..7dffb43cea 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java @@ -25,7 +25,9 @@ import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.util.ConcurrentReferenceHashMap; +import org.thingsboard.rule.engine.api.AttributesDeleteRequest; import org.thingsboard.rule.engine.api.AttributesSaveRequest; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntityType; @@ -284,7 +286,7 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen (startTime == 0 && endTime > lastUpdateTs) || (startTime < lastUpdateTs && endTime > lastUpdateTs); }).collect(Collectors.toList()); - tsSubService.save(AttributesSaveRequest.builder() + tsSubService.saveAttributes(AttributesSaveRequest.builder() .tenantId(entityView.getTenantId()) .entityId(entityId) .scope(scope) @@ -340,15 +342,22 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen }, MoreExecutors.directExecutor()); return Futures.transform(latestFuture, latestValues -> { if (latestValues != null && !latestValues.isEmpty()) { - tsSubService.saveLatestAndNotify(entityView.getTenantId(), entityId, latestValues, new FutureCallback() { - @Override - public void onSuccess(@Nullable Void tmp) { - } - - @Override - public void onFailure(Throwable t) { - } - }); + tsSubService.saveTimeseries(TimeseriesSaveRequest.builder() + .tenantId(entityView.getTenantId()) + .entityId(entityId) + .entries(latestValues) + .onlyLatest(true) + .callback(new FutureCallback() { + @Override + public void onSuccess(@Nullable Void tmp) { + } + + @Override + public void onFailure(Throwable t) { + log.error("[{}][{}] Failed to save entity view latest timeseries: {}", tenantId, entityView.getId(), latestValues, t); + } + }) + .build()); } return null; }, MoreExecutors.directExecutor()); @@ -358,27 +367,33 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen EntityViewId entityId = entityView.getId(); SettableFuture resultFuture = SettableFuture.create(); if (keys != null && !keys.isEmpty()) { - tsSubService.deleteAndNotify(entityView.getTenantId(), entityId, scope, keys, new FutureCallback() { - @Override - public void onSuccess(@Nullable Void tmp) { - try { - logAttributesDeleted(entityView.getTenantId(), user, entityId, scope, keys, null); - } catch (ThingsboardException e) { - log.error("Failed to log attribute delete", e); - } - resultFuture.set(tmp); - } - - @Override - public void onFailure(Throwable t) { - try { - logAttributesDeleted(entityView.getTenantId(), user, entityId, scope, keys, t); - } catch (ThingsboardException e) { - log.error("Failed to log attribute delete", e); - } - resultFuture.setException(t); - } - }); + tsSubService.deleteAttributes(AttributesDeleteRequest.builder() + .tenantId(entityView.getTenantId()) + .entityId(entityId) + .scope(scope) + .keys(keys) + .callback(new FutureCallback<>() { + @Override + public void onSuccess(@Nullable Void tmp) { + try { + logAttributesDeleted(entityView.getTenantId(), user, entityId, scope, keys, null); + } catch (ThingsboardException e) { + log.error("Failed to log attribute delete", e); + } + resultFuture.set(tmp); + } + + @Override + public void onFailure(Throwable t) { + try { + logAttributesDeleted(entityView.getTenantId(), user, entityId, scope, keys, t); + } catch (ThingsboardException e) { + log.error("Failed to log attribute delete", e); + } + resultFuture.setException(t); + } + }) + .build()); } else { resultFuture.set(null); } diff --git a/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java b/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java index 3db4e53379..3aedad3ddb 100644 --- a/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java @@ -20,6 +20,7 @@ import jakarta.annotation.Nullable; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; +import org.thingsboard.rule.engine.api.AttributesDeleteRequest; import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; @@ -262,7 +263,7 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(getTargetTelemetryKey(firmware.getType(), TS), ts))); telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), OtaPackageUpdateStatus.QUEUED.name()))); - telemetryService.save(TimeseriesSaveRequest.builder() + telemetryService.saveTimeseries(TimeseriesSaveRequest.builder() .tenantId(tenantId) .entityId(deviceId) .entries(telemetry) @@ -288,7 +289,7 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(getTelemetryKey(otaPackageType, STATE), OtaPackageUpdateStatus.INITIATED.name())); - telemetryService.save(TimeseriesSaveRequest.builder() + telemetryService.saveTimeseries(TimeseriesSaveRequest.builder() .tenantId(tenantId) .entityId(deviceId) .entry(status) @@ -347,7 +348,7 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { remove(device, otaPackageType, attrToRemove); - telemetryService.save(AttributesSaveRequest.builder() + telemetryService.saveAttributes(AttributesSaveRequest.builder() .tenantId(tenantId) .entityId(deviceId) .scope(AttributeScope.SHARED_SCOPE) @@ -371,8 +372,12 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { } private void remove(Device device, OtaPackageType otaPackageType, List attributesKeys) { - telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), AttributeScope.SHARED_SCOPE, attributesKeys, - new FutureCallback<>() { + telemetryService.deleteAttributes(AttributesDeleteRequest.builder() + .tenantId(device.getTenantId()) + .entityId(device.getId()) + .scope(AttributeScope.SHARED_SCOPE) + .keys(attributesKeys) + .callback(new FutureCallback<>() { @Override public void onSuccess(@Nullable Void tmp) { log.trace("[{}] Success remove target {} attributes!", device.getId(), otaPackageType); @@ -383,7 +388,8 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { public void onFailure(Throwable t) { log.error("[{}] Failed to remove target {} attributes!", device.getId(), otaPackageType, t); } - }); + }) + .build()); } } diff --git a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java index 8280cf44b8..da62e1bd01 100644 --- a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java @@ -877,7 +877,7 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService(deviceId, kvEntry)) .build()); } else { - tsSubService.save(AttributesSaveRequest.builder() + tsSubService.saveAttributes(AttributesSaveRequest.builder() .tenantId(TenantId.SYS_TENANT_ID) .entityId(deviceId) .scope(AttributeScope.SERVER_SCOPE) diff --git a/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java b/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java index 2a214388a1..91766c0f5d 100644 --- a/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java +++ b/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java @@ -89,7 +89,7 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS if (!tsList.isEmpty()) { long ttl = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getQueueStatsTtlDays); ttl = TimeUnit.DAYS.toSeconds(ttl); - tsService.saveInternal(TimeseriesSaveRequest.builder() + tsService.saveTimeseriesInternal(TimeseriesSaveRequest.builder() .tenantId(tenantId) .entityId(queueStatsId) .entries(tsList) @@ -109,7 +109,7 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS TsKvEntry tsKv = new BasicTsKvEntry(e.getTs(), new JsonDataEntry(RULE_ENGINE_EXCEPTION, e.toJsonString(maxErrorMessageLength))); long ttl = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getRuleEngineExceptionsTtlDays); ttl = TimeUnit.DAYS.toSeconds(ttl); - tsService.saveInternal(TimeseriesSaveRequest.builder() + tsService.saveTimeseriesInternal(TimeseriesSaveRequest.builder() .tenantId(tenantId) .entityId(getQueueStatsId(tenantId, queueName)) .entry(tsKv) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java index f3e8266ce6..3edb7c9be0 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java @@ -208,7 +208,7 @@ public abstract class AbstractBulkImportService { TenantProfile tenantProfile = tenantProfileCache.get(tenantId); long tenantTtl = TimeUnit.DAYS.toSeconds(((DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration()).getDefaultStorageTtlDays()); - tsSubscriptionService.save(TimeseriesSaveRequest.builder() + tsSubscriptionService.saveTimeseries(TimeseriesSaveRequest.builder() .tenantId(tenantId) .customerId(user.getCustomerId()) .entityId(entityId) @@ -238,7 +238,7 @@ public abstract class AbstractBulkImportService attributes = new ArrayList<>(JsonConverter.convertToAttributes(kvsEntry.getValue())); accessValidator.validateEntityAndCallback(user, Operation.WRITE_ATTRIBUTES, entity.getId(), (result, tenantId, entityId) -> { - tsSubscriptionService.save(AttributesSaveRequest.builder() + tsSubscriptionService.saveAttributes(AttributesSaveRequest.builder() .tenantId(tenantId) .entityId(entityId) .scope(AttributeScope.valueOf(scope)) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java index 199f54cd12..0d9c67823a 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java @@ -258,7 +258,7 @@ public abstract class BaseEntityImportService telemetry) { ApiUsageState apiUsageState = apiUsageStateClient.getApiUsageState(TenantId.SYS_TENANT_ID); - telemetryService.saveInternal(TimeseriesSaveRequest.builder() + telemetryService.saveTimeseriesInternal(TimeseriesSaveRequest.builder() .tenantId(TenantId.SYS_TENANT_ID) .entityId(apiUsageState.getId()) .entries(telemetry) diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java index 793aabc6cc..ad7f963894 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java @@ -27,11 +27,11 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.rule.engine.api.AttributesDeleteRequest; import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.data.ApiUsageRecordKey; -import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.id.CustomerId; @@ -111,27 +111,31 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer } @Override - public void save(TimeseriesSaveRequest request) { + public void saveTimeseries(TimeseriesSaveRequest request) { TenantId tenantId = request.getTenantId(); EntityId entityId = request.getEntityId(); checkInternalEntity(entityId); boolean sysTenant = TenantId.SYS_TENANT_ID.equals(tenantId) || tenantId == null; - if (sysTenant || apiUsageStateService.getApiUsageState(tenantId).isDbStorageEnabled()) { + if (sysTenant || request.isOnlyLatest() || apiUsageStateService.getApiUsageState(tenantId).isDbStorageEnabled()) { KvUtils.validate(request.getEntries(), valueNoXssValidation); - FutureCallback callback = getApiUsageCallback(tenantId, request.getCustomerId(), sysTenant, request.getCallback()); - ListenableFuture future = saveInternal(request); - Futures.addCallback(future, callback, tsCallBackExecutor); + ListenableFuture future = saveTimeseriesInternal(request); + if (!request.isOnlyLatest()) { + FutureCallback callback = getApiUsageCallback(tenantId, request.getCustomerId(), sysTenant, request.getCallback()); + Futures.addCallback(future, callback, tsCallBackExecutor); + } } else { request.getCallback().onFailure(new RuntimeException("DB storage writes are disabled due to API limits!")); } } @Override - public ListenableFuture saveInternal(TimeseriesSaveRequest request) { + public ListenableFuture saveTimeseriesInternal(TimeseriesSaveRequest request) { TenantId tenantId = request.getTenantId(); EntityId entityId = request.getEntityId(); ListenableFuture saveFuture; - if (request.isSaveLatest()) { + if (request.isOnlyLatest()) { + saveFuture = Futures.transform(tsService.saveLatest(tenantId, entityId, request.getEntries()), result -> 0, MoreExecutors.directExecutor()); + } else if (request.isSaveLatest()) { saveFuture = tsService.save(tenantId, entityId, request.getEntries(), request.getTtl()); } else { saveFuture = tsService.saveWithoutLatest(tenantId, entityId, request.getEntries(), request.getTtl()); @@ -139,63 +143,37 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer addMainCallback(saveFuture, request.getCallback()); addWsCallback(saveFuture, success -> onTimeSeriesUpdate(tenantId, entityId, request.getEntries())); - if (request.isSaveLatest()) { + if (request.isSaveLatest() && !request.isOnlyLatest()) { addEntityViewCallback(tenantId, entityId, request.getEntries()); } return saveFuture; } @Override - public void save(AttributesSaveRequest request) { + public void saveAttributes(AttributesSaveRequest request) { checkInternalEntity(request.getEntityId()); - saveInternal(request); + saveAttributesInternal(request); } @Override - public void saveInternal(AttributesSaveRequest request) { + public void saveAttributesInternal(AttributesSaveRequest request) { log.trace("Executing saveInternal [{}]", request); ListenableFuture> saveFuture = attrService.save(request.getTenantId(), request.getEntityId(), request.getScope(), request.getEntries()); - addVoidCallback(saveFuture, request.getCallback()); + addMainCallback(saveFuture, request.getCallback()); addWsCallback(saveFuture, success -> onAttributesUpdate(request.getTenantId(), request.getEntityId(), request.getScope().name(), request.getEntries(), request.isNotifyDevice())); } @Override - public void saveLatestAndNotify(TenantId tenantId, EntityId entityId, List ts, FutureCallback callback) { - checkInternalEntity(entityId); - saveLatestAndNotifyInternal(tenantId, entityId, ts, callback); - } - - @Override - public void saveLatestAndNotifyInternal(TenantId tenantId, EntityId entityId, List ts, FutureCallback callback) { - ListenableFuture> saveFuture = tsService.saveLatest(tenantId, entityId, ts); - addVoidCallback(saveFuture, callback); - addWsCallback(saveFuture, success -> onTimeSeriesUpdate(tenantId, entityId, ts)); - } - - @Override - public void deleteAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List keys, FutureCallback callback) { - checkInternalEntity(entityId); - deleteAndNotifyInternal(tenantId, entityId, scope, keys, false, callback); - } - - @Override - public void deleteAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List keys, boolean notifyDevice, FutureCallback callback) { - checkInternalEntity(entityId); - deleteAndNotifyInternal(tenantId, entityId, scope, keys, notifyDevice, callback); - } - - @Override - public void deleteAndNotifyInternal(TenantId tenantId, EntityId entityId, String scope, List keys, boolean notifyDevice, FutureCallback callback) { - ListenableFuture> deleteFuture = attrService.removeAll(tenantId, entityId, scope, keys); - addVoidCallback(deleteFuture, callback); - addWsCallback(deleteFuture, success -> onAttributesDelete(tenantId, entityId, scope, keys, notifyDevice)); + public void deleteAttributes(AttributesDeleteRequest request) { + checkInternalEntity(request.getEntityId()); + deleteAttributesInternal(request); } @Override - public void deleteAndNotifyInternal(TenantId tenantId, EntityId entityId, AttributeScope scope, List keys, boolean notifyDevice, FutureCallback callback) { - ListenableFuture> deleteFuture = attrService.removeAll(tenantId, entityId, scope, keys); - addVoidCallback(deleteFuture, callback); - addWsCallback(deleteFuture, success -> onAttributesDelete(tenantId, entityId, scope.name(), keys, notifyDevice)); + public void deleteAttributesInternal(AttributesDeleteRequest request) { + ListenableFuture> deleteFuture = attrService.removeAll(request.getTenantId(), request.getEntityId(), request.getScope(), request.getKeys()); + addMainCallback(deleteFuture, request.getCallback()); + addWsCallback(deleteFuture, success -> onAttributesDelete(request.getTenantId(), request.getEntityId(), request.getScope().name(), request.getKeys(), request.isNotifyDevice())); } @Override @@ -207,7 +185,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer @Override public void deleteLatestInternal(TenantId tenantId, EntityId entityId, List keys, FutureCallback callback) { ListenableFuture> deleteFuture = tsService.removeLatest(tenantId, entityId, keys); - addVoidCallback(deleteFuture, callback); + addMainCallback(deleteFuture, callback); } @Override @@ -229,7 +207,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer @Override public void deleteTimeseriesAndNotify(TenantId tenantId, EntityId entityId, List keys, List deleteTsKvQueries, FutureCallback callback) { ListenableFuture> deleteFuture = tsService.remove(tenantId, entityId, deleteTsKvQueries); - addVoidCallback(deleteFuture, callback); + addMainCallback(deleteFuture, callback); addWsCallback(deleteFuture, list -> onTimeSeriesDelete(tenantId, entityId, keys, list)); } @@ -260,15 +238,21 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer } } if (!entityViewLatest.isEmpty()) { - saveLatestAndNotify(tenantId, entityView.getId(), entityViewLatest, new FutureCallback<>() { - @Override - public void onSuccess(@Nullable Void tmp) { - } - - @Override - public void onFailure(Throwable t) { - } - }); + saveTimeseries(TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .entityId(entityView.getId()) + .entries(entityViewLatest) + .onlyLatest(true) + .callback(new FutureCallback<>() { + @Override + public void onSuccess(@Nullable Void tmp) {} + + @Override + public void onFailure(Throwable t) { + log.error("[{}][{}] Failed to save entity view latest timeseries: {}", tenantId, entityView.getId(), entityViewLatest, t); + } + }) + .build()); } } } @@ -328,22 +312,8 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer }); } - private void addVoidCallback(ListenableFuture saveFuture, final FutureCallback callback) { - if (callback == null) return; - Futures.addCallback(saveFuture, new FutureCallback() { - @Override - public void onSuccess(@Nullable S result) { - callback.onSuccess(null); - } - - @Override - public void onFailure(Throwable t) { - callback.onFailure(t); - } - }, tsCallBackExecutor); - } - private void addMainCallback(ListenableFuture saveFuture, final FutureCallback callback) { + if (callback == null) return; Futures.addCallback(saveFuture, new FutureCallback() { @Override public void onSuccess(@Nullable S result) { diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java index 20eff8b6b5..1db6a86507 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java @@ -17,13 +17,12 @@ package org.thingsboard.server.service.telemetry; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.rule.engine.api.AttributesDeleteRequest; import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; -import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.kv.TsKvEntry; import java.util.List; @@ -32,16 +31,11 @@ import java.util.List; */ public interface InternalTelemetryService extends RuleEngineTelemetryService { - ListenableFuture saveInternal(TimeseriesSaveRequest request); + ListenableFuture saveTimeseriesInternal(TimeseriesSaveRequest request); - void saveInternal(AttributesSaveRequest request); + void saveAttributesInternal(AttributesSaveRequest request); - void saveLatestAndNotifyInternal(TenantId tenantId, EntityId entityId, List ts, FutureCallback callback); - - @Deprecated(since = "3.7.0") - void deleteAndNotifyInternal(TenantId tenantId, EntityId entityId, String scope, List keys, boolean notifyDevice, FutureCallback callback); - - void deleteAndNotifyInternal(TenantId tenantId, EntityId entityId, AttributeScope scope, List keys, boolean notifyDevice, FutureCallback callback); + void deleteAttributesInternal(AttributesDeleteRequest request); void deleteLatestInternal(TenantId tenantId, EntityId entityId, List keys, FutureCallback callback); diff --git a/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java b/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java index 05e7132ce8..b64c87ccbd 100644 --- a/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java @@ -806,7 +806,7 @@ public class WebsocketApiTest extends AbstractControllerTest { private void sendTelemetry(Device device, List tsData) throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); - tsService.save(TimeseriesSaveRequest.builder() + tsService.saveTimeseries(TimeseriesSaveRequest.builder() .tenantId(device.getTenantId()) .entityId(device.getId()) .entries(tsData) @@ -833,7 +833,7 @@ public class WebsocketApiTest extends AbstractControllerTest { private void sendAttributes(TenantId tenantId, EntityId entityId, TbAttributeSubscriptionScope scope, List attrData) throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); - tsService.save(AttributesSaveRequest.builder() + tsService.saveAttributes(AttributesSaveRequest.builder() .tenantId(tenantId) .entityId(entityId) .scope(scope.getAttributeScope()) diff --git a/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java b/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java index b7207da23a..b58d19e0e5 100644 --- a/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java @@ -24,7 +24,6 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.test.util.ReflectionTestUtils; @@ -211,7 +210,7 @@ public class DefaultDeviceStateServiceTest { service.onDeviceConnect(tenantId, deviceId, lastConnectTime); // THEN - then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && request.getScope().equals(AttributeScope.SERVER_SCOPE) && request.getEntries().get(0).getKey().equals(LAST_CONNECT_TIME) && @@ -298,7 +297,7 @@ public class DefaultDeviceStateServiceTest { service.onDeviceDisconnect(tenantId, deviceId, lastDisconnectTime); // THEN - then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && request.getScope().equals(AttributeScope.SERVER_SCOPE) && request.getEntries().get(0).getKey().equals(LAST_DISCONNECT_TIME) && @@ -421,13 +420,13 @@ public class DefaultDeviceStateServiceTest { service.onDeviceInactivity(tenantId, deviceId, lastInactivityTime); // THEN - then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && request.getScope().equals(AttributeScope.SERVER_SCOPE) && request.getEntries().get(0).getKey().equals(INACTIVITY_ALARM_TIME) && request.getEntries().get(0).getValue().equals(lastInactivityTime) )); - then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && request.getScope().equals(AttributeScope.SERVER_SCOPE) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && @@ -465,12 +464,12 @@ public class DefaultDeviceStateServiceTest { service.updateInactivityStateIfExpired(System.currentTimeMillis(), deviceId, deviceStateData); // THEN - then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && request.getScope().equals(AttributeScope.SERVER_SCOPE) && request.getEntries().get(0).getKey().equals(INACTIVITY_ALARM_TIME) )); - then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && request.getScope().equals(AttributeScope.SERVER_SCOPE) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && @@ -627,7 +626,7 @@ public class DefaultDeviceStateServiceTest { long newTimeout = System.currentTimeMillis() - deviceState.getLastActivityTime() + increase; service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, newTimeout); - verify(telemetrySubscriptionService, never()).save(argThat((ArgumentMatcher) request -> + verify(telemetrySubscriptionService, never()).saveAttributes(argThat(request -> request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) )); Thread.sleep(defaultTimeout + increase); @@ -668,7 +667,7 @@ public class DefaultDeviceStateServiceTest { long newTimeout = 1; Thread.sleep(newTimeout); - verify(telemetrySubscriptionService, never()).save(argThat((ArgumentMatcher) request -> + verify(telemetrySubscriptionService, never()).saveAttributes(argThat(request -> request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) )); } @@ -730,13 +729,13 @@ public class DefaultDeviceStateServiceTest { long newTimeout = 1; service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, newTimeout); - verify(telemetrySubscriptionService, never()).save(argThat((ArgumentMatcher) request -> + verify(telemetrySubscriptionService, never()).saveAttributes(argThat(request -> request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) )); } private void activityVerify(boolean isActive) { - verify(telemetrySubscriptionService).save(argThat((ArgumentMatcher) request -> + verify(telemetrySubscriptionService).saveAttributes(argThat(request -> request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && request.getEntries().get(0).getValue().equals(isActive) @@ -786,7 +785,7 @@ public class DefaultDeviceStateServiceTest { // THEN assertThat(deviceState.isActive()).isEqualTo(true); assertThat(deviceState.getLastActivityTime()).isEqualTo(lastReportedActivity); - then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(LAST_ACTIVITY_TIME) && request.getEntries().get(0).getValue().equals(lastReportedActivity) @@ -794,7 +793,7 @@ public class DefaultDeviceStateServiceTest { assertThat(deviceState.getLastInactivityAlarmTime()).isEqualTo(expectedInactivityAlarmTime); if (shouldSetInactivityAlarmTimeToZero) { - then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(INACTIVITY_ALARM_TIME) && request.getEntries().get(0).getValue().equals(0L) @@ -802,7 +801,7 @@ public class DefaultDeviceStateServiceTest { } if (shouldUpdateActivityStateToActive) { - then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && request.getEntries().get(0).getValue().equals(true) @@ -886,7 +885,7 @@ public class DefaultDeviceStateServiceTest { assertThat(deviceState.getInactivityTimeout()).isEqualTo(newInactivityTimeout); assertThat(deviceState.isActive()).isEqualTo(expectedActivityState); if (activityState && !expectedActivityState) { - then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && request.getEntries().get(0).getValue().equals(false) )); @@ -984,7 +983,7 @@ public class DefaultDeviceStateServiceTest { assertThat(state.getLastInactivityAlarmTime()).isEqualTo(expectedLastInactivityAlarmTime); if (shouldUpdateActivityStateToInactive) { - then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && request.getEntries().get(0).getValue().equals(false) )); @@ -1002,7 +1001,7 @@ public class DefaultDeviceStateServiceTest { assertThat(actualNotification.getDeviceId()).isEqualTo(deviceId); assertThat(actualNotification.isActive()).isFalse(); - then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> request.getTenantId().equals(TenantId.SYS_TENANT_ID) && request.getEntityId().equals(deviceId) && request.getScope().equals(AttributeScope.SERVER_SCOPE) && request.getEntries().get(0).getKey().equals(INACTIVITY_ALARM_TIME) && @@ -1133,7 +1132,7 @@ public class DefaultDeviceStateServiceTest { // THEN await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { assertThat(service.deviceStates.get(deviceId).getState().isActive()).isEqualTo(false); - then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && request.getEntries().get(0).getValue().equals(false) )); @@ -1164,7 +1163,7 @@ public class DefaultDeviceStateServiceTest { // THEN ArgumentCaptor attributeRequestCaptor = ArgumentCaptor.forClass(AttributesSaveRequest.class); - then(telemetrySubscriptionService).should(times(2)).save(attributeRequestCaptor.capture()); + then(telemetrySubscriptionService).should(times(2)).saveAttributes(attributeRequestCaptor.capture()); await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { assertThat(service.deviceStates.get(deviceId).getState().isActive()).isEqualTo(true); @@ -1231,7 +1230,7 @@ public class DefaultDeviceStateServiceTest { // THEN await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { assertThat(service.deviceStates.get(deviceId).getState().isActive()).isEqualTo(true); - then(telemetrySubscriptionService).should().save(argThat((ArgumentMatcher) request -> + then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && request.getEntries().get(0).getValue().equals(true) )); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java index a205ee9b84..8668551fbb 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java @@ -37,16 +37,10 @@ public interface AttributesService { ListenableFuture> findAll(TenantId tenantId, EntityId entityId, AttributeScope scope); - @Deprecated(since = "3.7.0") - ListenableFuture> save(TenantId tenantId, EntityId entityId, String scope, List attributes); - ListenableFuture> save(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributes); ListenableFuture save(TenantId tenantId, EntityId entityId, AttributeScope scope, AttributeKvEntry attribute); - @Deprecated(since = "3.7.0") - ListenableFuture> removeAll(TenantId tenantId, EntityId entityId, String scope, List attributeKeys); - ListenableFuture> removeAll(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributeKeys); List findAllKeysByDeviceProfileId(TenantId tenantId, DeviceProfileId deviceProfileId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java index 88d6757de3..0694178540 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java @@ -101,14 +101,6 @@ public class BaseAttributesService implements AttributesService { return attributesDao.save(tenantId, entityId, scope, attribute); } - @Override - public ListenableFuture> save(TenantId tenantId, EntityId entityId, String scope, List attributes) { - validate(entityId, scope); - AttributeUtils.validate(attributes, valueNoXssValidation); - List> saveFutures = attributes.stream().map(attribute -> attributesDao.save(tenantId, entityId, AttributeScope.valueOf(scope), attribute)).collect(Collectors.toList()); - return Futures.allAsList(saveFutures); - } - @Override public ListenableFuture> save(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributes) { validate(entityId, scope); @@ -117,12 +109,6 @@ public class BaseAttributesService implements AttributesService { return Futures.allAsList(saveFutures); } - @Override - public ListenableFuture> removeAll(TenantId tenantId, EntityId entityId, String scope, List attributeKeys) { - validate(entityId, scope); - return Futures.allAsList(attributesDao.removeAll(tenantId, entityId, AttributeScope.valueOf(scope), attributeKeys)); - } - @Override public ListenableFuture> removeAll(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributeKeys) { validate(entityId, scope); diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java index 3de90355ed..1ebd5c0ba4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java @@ -222,11 +222,6 @@ public class CachedAttributesService implements AttributesService { return doSave(tenantId, entityId, scope, attribute); } - @Override - public ListenableFuture> save(TenantId tenantId, EntityId entityId, String scope, List attributes) { - return save(tenantId, entityId, AttributeScope.valueOf(scope), attributes); - } - @Override public ListenableFuture> save(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributes) { validate(entityId, scope); @@ -255,11 +250,6 @@ public class CachedAttributesService implements AttributesService { log.trace("[{}][{}][{}] after cache put.", entityId, scope, key); } - @Override - public ListenableFuture> removeAll(TenantId tenantId, EntityId entityId, String scope, List attributeKeys) { - return removeAll(tenantId, entityId, AttributeScope.valueOf(scope), attributeKeys); - } - @Override public ListenableFuture> removeAll(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributeKeys) { validate(entityId, scope); diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/AttributesDeleteRequest.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/AttributesDeleteRequest.java new file mode 100644 index 0000000000..118c62e78c --- /dev/null +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/AttributesDeleteRequest.java @@ -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. + */ +package org.thingsboard.rule.engine.api; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.SettableFuture; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; +import org.thingsboard.server.common.data.AttributeScope; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; + +import java.util.List; + +@Getter +@ToString +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class AttributesDeleteRequest { + + private final TenantId tenantId; + private final EntityId entityId; + private final AttributeScope scope; + private final List keys; + private final boolean notifyDevice; + private final FutureCallback callback; + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private TenantId tenantId; + private EntityId entityId; + private AttributeScope scope; + private List keys; + private boolean notifyDevice; + private FutureCallback callback; + + Builder() {} + + public Builder tenantId(TenantId tenantId) { + this.tenantId = tenantId; + return this; + } + + public Builder entityId(EntityId entityId) { + this.entityId = entityId; + return this; + } + + public Builder scope(AttributeScope scope) { + this.scope = scope; + return this; + } + + @Deprecated + public Builder scope(String scope) { + try { + this.scope = AttributeScope.valueOf(scope); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Invalid attribute scope '" + scope + "'"); + } + return this; + } + + public Builder keys(List keys) { + this.keys = keys; + return this; + } + + public Builder notifyDevice(boolean notifyDevice) { + this.notifyDevice = notifyDevice; + return this; + } + + public Builder callback(FutureCallback callback) { + this.callback = callback; + return this; + } + + public Builder future(SettableFuture future) { + return callback(new FutureCallback<>() { + @Override + public void onSuccess(Void result) { + future.set(result); + } + + @Override + public void onFailure(Throwable t) { + future.setException(t); + } + }); + } + + public AttributesDeleteRequest build() { + return new AttributesDeleteRequest(tenantId, entityId, scope, keys, notifyDevice, callback); + } + + } + +} diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java index 420c0c659b..17ed6aaa13 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java @@ -16,11 +16,9 @@ package org.thingsboard.rule.engine.api; import com.google.common.util.concurrent.FutureCallback; -import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; -import org.thingsboard.server.common.data.kv.TsKvEntry; import java.util.Collection; import java.util.List; @@ -30,15 +28,11 @@ import java.util.List; */ public interface RuleEngineTelemetryService { - void save(TimeseriesSaveRequest request); + void saveTimeseries(TimeseriesSaveRequest request); - void save(AttributesSaveRequest request); + void saveAttributes(AttributesSaveRequest request); - void saveLatestAndNotify(TenantId tenantId, EntityId entityId, List ts, FutureCallback callback); - - void deleteAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List keys, FutureCallback callback); - - void deleteAndNotify(TenantId tenantId, EntityId entityId, AttributeScope scope, List keys, boolean notifyDevice, FutureCallback callback); + void deleteAttributes(AttributesDeleteRequest request); void deleteLatest(TenantId tenantId, EntityId entityId, List keys, FutureCallback callback); diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java index 57fba9232f..2b5881212d 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java @@ -39,6 +39,7 @@ public class TimeseriesSaveRequest { private final List entries; private final long ttl; private final boolean saveLatest; + private final boolean onlyLatest; private final FutureCallback callback; public static Builder builder() { @@ -54,6 +55,7 @@ public class TimeseriesSaveRequest { private long ttl; private FutureCallback callback; private boolean saveLatest = true; + private boolean onlyLatest; Builder() {} @@ -95,6 +97,12 @@ public class TimeseriesSaveRequest { return this; } + public Builder onlyLatest(boolean onlyLatest) { + this.onlyLatest = onlyLatest; + this.saveLatest = true; + return this; + } + public Builder callback(FutureCallback callback) { this.callback = callback; return this; @@ -115,7 +123,7 @@ public class TimeseriesSaveRequest { } public TimeseriesSaveRequest build() { - return new TimeseriesSaveRequest(tenantId, customerId, entityId, entries, ttl, saveLatest, callback); + return new TimeseriesSaveRequest(tenantId, customerId, entityId, entries, ttl, saveLatest, onlyLatest, callback); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java index e18ced7ee0..a4b0227551 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java @@ -23,6 +23,7 @@ import com.google.gson.JsonPrimitive; import jakarta.annotation.Nullable; import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.DonAsynchron; +import org.thingsboard.rule.engine.api.AttributesDeleteRequest; import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.EmptyNodeConfiguration; import org.thingsboard.rule.engine.api.RuleNode; @@ -106,14 +107,19 @@ public class TbCopyAttributesToEntityViewNode implements TbNode { List filteredAttributes = attributes.stream().filter(attr -> attributeContainsInEntityView(scope, attr, entityView)).collect(Collectors.toList()); if (!filteredAttributes.isEmpty()) { - ctx.getTelemetryService().deleteAndNotify(ctx.getTenantId(), entityView.getId(), scope, filteredAttributes, - getFutureCallback(ctx, msg, entityView)); + ctx.getTelemetryService().deleteAttributes(AttributesDeleteRequest.builder() + .tenantId(ctx.getTenantId()) + .entityId(entityView.getId()) + .scope(scope) + .keys(filteredAttributes) + .callback(getFutureCallback(ctx, msg, entityView)) + .build()); } } else { Set attributes = JsonConverter.convertToAttributes(JsonParser.parseString(msg.getData())); List filteredAttributes = attributes.stream().filter(attr -> attributeContainsInEntityView(scope, attr.getKey(), entityView)).collect(Collectors.toList()); - ctx.getTelemetryService().save(AttributesSaveRequest.builder() + ctx.getTelemetryService().saveAttributes(AttributesSaveRequest.builder() .tenantId(ctx.getTenantId()) .entityId(entityView.getId()) .scope(scope) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java index 1d18290129..2ce2212054 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java @@ -145,7 +145,7 @@ public class TbMathNode implements TbNode { private ListenableFuture saveTimeSeries(TbContext ctx, TbMsg msg, double result, TbMathResult mathResultDef) { final BasicTsKvEntry basicTsKvEntry = new BasicTsKvEntry(System.currentTimeMillis(), new DoubleDataEntry(mathResultDef.getKey(), result)); SettableFuture future = SettableFuture.create(); - ctx.getTelemetryService().save(TimeseriesSaveRequest.builder() + ctx.getTelemetryService().saveTimeseries(TimeseriesSaveRequest.builder() .tenantId(ctx.getTenantId()) .entityId(msg.getOriginator()) .entry(basicTsKvEntry) @@ -165,7 +165,7 @@ public class TbMathNode implements TbNode { kvEntry = new DoubleDataEntry(mathResultDef.getKey(), value); } SettableFuture future = SettableFuture.create(); - ctx.getTelemetryService().save(AttributesSaveRequest.builder() + ctx.getTelemetryService().saveAttributes(AttributesSaveRequest.builder() .tenantId(ctx.getTenantId()) .entityId(msg.getOriginator()) .scope(attributeScope) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java index 2250e0bbdd..e83a125265 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java @@ -119,7 +119,7 @@ public class TbMsgAttributesNode implements TbNode { FutureCallback callback = sendAttributesUpdateNotification ? new AttributesUpdateNodeCallback(ctx, msg, scope.name(), attributes) : new TelemetryNodeCallback(ctx, msg); - ctx.getTelemetryService().save(AttributesSaveRequest.builder() + ctx.getTelemetryService().saveAttributes(AttributesSaveRequest.builder() .tenantId(ctx.getTenantId()) .entityId(msg.getOriginator()) .scope(scope) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNode.java index 66faa80f9f..f1f1a29f3c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNode.java @@ -16,6 +16,7 @@ package org.thingsboard.rule.engine.telemetry; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.rule.engine.api.AttributesDeleteRequest; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; @@ -70,16 +71,16 @@ public class TbMsgDeleteAttributesNode implements TbNode { ctx.tellSuccess(msg); } else { AttributeScope scope = getScope(msg.getMetaData().getValue(SCOPE)); - ctx.getTelemetryService().deleteAndNotify( - ctx.getTenantId(), - msg.getOriginator(), - scope, - keysToDelete, - checkNotifyDevice(msg.getMetaData().getValue(NOTIFY_DEVICE_METADATA_KEY), scope), - config.isSendAttributesDeletedNotification() ? + ctx.getTelemetryService().deleteAttributes(AttributesDeleteRequest.builder() + .tenantId(ctx.getTenantId()) + .entityId(msg.getOriginator()) + .scope(scope) + .keys(keysToDelete) + .notifyDevice(checkNotifyDevice(msg.getMetaData().getValue(NOTIFY_DEVICE_METADATA_KEY), scope)) + .callback(config.isSendAttributesDeletedNotification() ? new AttributesDeleteNodeCallback(ctx, msg, scope.name(), keysToDelete) : - new TelemetryNodeCallback(ctx, msg) - ); + new TelemetryNodeCallback(ctx, msg)) + .build()); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java index b0d5e23b50..27f45feb47 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java @@ -105,7 +105,7 @@ public class TbMsgTimeseriesNode implements TbNode { if (ttl == 0L) { ttl = tenantProfileDefaultStorageTtl; } - ctx.getTelemetryService().save(TimeseriesSaveRequest.builder() + ctx.getTelemetryService().saveTimeseries(TimeseriesSaveRequest.builder() .tenantId(ctx.getTenantId()) .customerId(msg.getCustomerId()) .entityId(msg.getOriginator()) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNodeTest.java index fcd6083eb2..24dd064b57 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNodeTest.java @@ -15,7 +15,6 @@ */ package org.thingsboard.rule.engine.action; -import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -24,9 +23,9 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.ThrowingConsumer; import org.mockito.junit.jupiter.MockitoExtension; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.AttributesDeleteRequest; import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.EmptyNodeConfiguration; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; @@ -56,7 +55,6 @@ import java.util.UUID; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.assertArg; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; @@ -118,7 +116,7 @@ public class TbCopyAttributesToEntityViewNodeTest { AttributesSaveRequest request = invocation.getArgument(0); request.getCallback().onSuccess(null); return null; - }).when(telemetryServiceMock).save(any(AttributesSaveRequest.class)); + }).when(telemetryServiceMock).saveAttributes(any(AttributesSaveRequest.class)); TbMsg newMsg = TbMsg.newMsg(msg, msg.getQueueName(), msg.getRuleChainId(), msg.getRuleNodeId()); // TODO: use newMsg() with any(TbMsgType.class), replace in other tests as well. doAnswer(invocation -> newMsg).when(ctxMock).newMsg(any(), any(String.class), any(), any(), any(), any()); @@ -126,7 +124,7 @@ public class TbCopyAttributesToEntityViewNodeTest { node.onMsg(ctxMock, msg); verify(entityViewServiceMock).findEntityViewsByTenantIdAndEntityIdAsync(eq(TENANT_ID), eq(DEVICE_ID)); - verify(telemetryServiceMock).save(assertArg((ThrowingConsumer) request -> { + verify(telemetryServiceMock).saveAttributes(assertArg(request -> { assertThat(request.getTenantId()).isEqualTo(TENANT_ID); assertThat(request.getEntityId()).isEqualTo(ENTITY_VIEW_ID); assertThat(request.getScope()).isEqualTo(AttributeScope.CLIENT_SCOPE); @@ -151,21 +149,23 @@ public class TbCopyAttributesToEntityViewNodeTest { mockEntityViewLookup(entityView); when(ctxMock.getTelemetryService()).thenReturn(telemetryServiceMock); doAnswer(invocation -> { - FutureCallback callback = invocation.getArgument(4); - callback.onSuccess(null); + AttributesDeleteRequest request = invocation.getArgument(0); + request.getCallback().onSuccess(null); return null; - }).when(telemetryServiceMock).deleteAndNotify(any(), any(), any(AttributeScope.class), anyList(), any(FutureCallback.class)); + }).when(telemetryServiceMock).deleteAttributes(any()); TbMsg newMsg = TbMsg.newMsg(msg, msg.getQueueName(), msg.getRuleChainId(), msg.getRuleNodeId()); doAnswer(invocation -> newMsg).when(ctxMock).newMsg(any(), any(String.class), any(), any(), any(), any()); node.onMsg(ctxMock, msg); verify(entityViewServiceMock).findEntityViewsByTenantIdAndEntityIdAsync(eq(TENANT_ID), eq(DEVICE_ID)); - ArgumentCaptor> filteredAttributesCaptor = ArgumentCaptor.forClass(List.class); - verify(telemetryServiceMock).deleteAndNotify(eq(TENANT_ID), eq(ENTITY_VIEW_ID), eq(AttributeScope.SERVER_SCOPE), filteredAttributesCaptor.capture(), any(FutureCallback.class)); - List filteredAttributesCaptorValue = filteredAttributesCaptor.getValue(); - assertThat(filteredAttributesCaptorValue.size()).isEqualTo(1); - assertThat(filteredAttributesCaptorValue.get(0)).isEqualTo("serverAttribute1"); + verify(telemetryServiceMock).deleteAttributes(assertArg(request -> { + assertThat(request.getTenantId()).isEqualTo(TENANT_ID); + assertThat(request.getEntityId()).isEqualTo(ENTITY_VIEW_ID); + assertThat(request.getScope()).isEqualTo(AttributeScope.SERVER_SCOPE); + assertThat(request.getKeys().size()).isEqualTo(1); + assertThat(request.getKeys().get(0)).isEqualTo("serverAttribute1"); + })); verify(ctxMock).ack(eq(msg)); verify(ctxMock).enqueueForTellNext(eq(newMsg), eq(TbNodeConnectionType.SUCCESS)); verifyNoMoreInteractions(ctxMock, entityViewServiceMock, telemetryServiceMock); @@ -202,14 +202,14 @@ public class TbCopyAttributesToEntityViewNodeTest { AttributesSaveRequest request = invocation.getArgument(0); request.getCallback().onSuccess(null); return null; - }).when(telemetryServiceMock).save(any(AttributesSaveRequest.class)); + }).when(telemetryServiceMock).saveAttributes(any(AttributesSaveRequest.class)); TbMsg newMsg = TbMsg.newMsg(msg, msg.getQueueName(), msg.getRuleChainId(), msg.getRuleNodeId()); doAnswer(invocation -> newMsg).when(ctxMock).newMsg(any(), any(String.class), any(), any(), any(), any()); node.onMsg(ctxMock, msg); verify(entityViewServiceMock).findEntityViewsByTenantIdAndEntityIdAsync(eq(TENANT_ID), eq(DEVICE_ID)); - verify(telemetryServiceMock).save(assertArg((ThrowingConsumer) request -> { + verify(telemetryServiceMock).saveAttributes(assertArg(request -> { assertThat(request.getTenantId()).isEqualTo(TENANT_ID); assertThat(request.getEntityId()).isEqualTo(ENTITY_VIEW_ID); assertThat(request.getScope()).isEqualTo(AttributeScope.CLIENT_SCOPE); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java index bec8946277..ad607fb262 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java @@ -441,13 +441,13 @@ public class TbMathNodeTest { AttributesSaveRequest request = invocation.getArgument(0); request.getCallback().onSuccess(null); return null; - }).when(telemetryService).save(any(AttributesSaveRequest.class)); + }).when(telemetryService).saveAttributes(any(AttributesSaveRequest.class)); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); - verify(telemetryService, times(1)).save(assertArg((ThrowingConsumer) request -> { + verify(telemetryService, times(1)).saveAttributes(assertArg(request -> { assertThat(request.getEntries()).singleElement().extracting(KvEntry::getValue).isInstanceOf(Double.class); })); @@ -471,13 +471,13 @@ public class TbMathNodeTest { TimeseriesSaveRequest request = invocation.getArgument(0); request.getCallback().onSuccess(null); return null; - }).when(telemetryService).save(any(TimeseriesSaveRequest.class)); + }).when(telemetryService).saveTimeseries(any(TimeseriesSaveRequest.class)); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); - verify(telemetryService, times(1)).save(assertArg((ThrowingConsumer) request -> { + verify(telemetryService, times(1)).saveTimeseries(assertArg(request -> { assertThat(request.getEntries()).size().isOne(); assertThat(request.isSaveLatest()).isTrue(); })); @@ -502,13 +502,13 @@ public class TbMathNodeTest { TimeseriesSaveRequest request = invocation.getArgument(0); request.getCallback().onSuccess(null); return null; - }).when(telemetryService).save(any(TimeseriesSaveRequest.class)); + }).when(telemetryService).saveTimeseries(any(TimeseriesSaveRequest.class)); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); - verify(telemetryService, times(1)).save(assertArg((ThrowingConsumer) request -> { + verify(telemetryService, times(1)).saveTimeseries(assertArg(request -> { assertThat(request.getEntries()).size().isOne(); assertThat(request.isSaveLatest()).isTrue(); })); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java index 74aec7b566..05c172d27f 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java @@ -22,10 +22,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.mockito.ThrowingConsumer; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.AbstractRuleNodeUpgradeTest; -import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; @@ -171,7 +169,7 @@ class TbMsgAttributesNodeTest extends AbstractRuleNodeUpgradeTest { node.saveAttr(testAttrList, ctxMock, testTbMsg, AttributeScope.SHARED_SCOPE, false); - verify(telemetryServiceMock, times(1)).save(assertArg((ThrowingConsumer) request -> { + verify(telemetryServiceMock, times(1)).saveAttributes(assertArg(request -> { assertThat(request.getTenantId()).isEqualTo(tenantId); assertThat(request.getEntityId()).isEqualTo(deviceId); assertThat(request.getScope()).isEqualTo(AttributeScope.SHARED_SCOPE); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNodeTest.java index 3d546c80f7..d1fd1690f6 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNodeTest.java @@ -21,11 +21,11 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.AttributesDeleteRequest; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; -import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.msg.TbMsgType; @@ -41,10 +41,9 @@ import java.util.function.Consumer; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.assertArg; import static org.mockito.BDDMockito.willAnswer; import static org.mockito.BDDMockito.willReturn; import static org.mockito.Mockito.mock; @@ -78,11 +77,10 @@ public class TbMsgDeleteAttributesNodeTest { willReturn(telemetryService).given(ctx).getTelemetryService(); willAnswer(invocation -> { - TelemetryNodeCallback callBack = invocation.getArgument(5); - callBack.onSuccess(null); + AttributesDeleteRequest request = invocation.getArgument(0); + request.getCallback().onSuccess(null); return null; - }).given(telemetryService).deleteAndNotify( - any(), any(), any(AttributeScope.class), anyList(), anyBoolean(), any()); + }).given(telemetryService).deleteAttributes(any()); } @AfterEach @@ -153,6 +151,8 @@ public class TbMsgDeleteAttributesNodeTest { } verify(ctx, times(1)).tellSuccess(newMsgCaptor.capture()); verify(ctx, never()).tellFailure(any(), any()); - verify(telemetryService, times(1)).deleteAndNotify(any(), any(), any(AttributeScope.class), anyList(), eq(notifyDevice || notifyDeviceMetadata), any()); + verify(telemetryService, times(1)).deleteAttributes(assertArg(request -> { + assertThat(request.isNotifyDevice()).isEqualTo(notifyDevice || notifyDeviceMetadata); + })); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java index a66c27e06a..d963eb65f4 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java @@ -130,12 +130,12 @@ public class TbMsgTimeseriesNodeTest { TimeseriesSaveRequest request = invocation.getArgument(0); request.getCallback().onSuccess(null); return null; - }).when(telemetryServiceMock).save(any(TimeseriesSaveRequest.class)); + }).when(telemetryServiceMock).saveTimeseries(any(TimeseriesSaveRequest.class)); node.onMsg(ctxMock, msg); List expectedList = getTsKvEntriesListWithTs(data, System.currentTimeMillis()); - verify(telemetryServiceMock).save(assertArg((ThrowingConsumer) request -> { + verify(telemetryServiceMock).saveTimeseries(assertArg(request -> { assertThat(request.getTenantId()).isEqualTo(TENANT_ID); assertThat(request.getCustomerId()).isNull(); assertThat(request.getEntityId()).isEqualTo(DEVICE_ID); @@ -171,12 +171,12 @@ public class TbMsgTimeseriesNodeTest { TimeseriesSaveRequest request = invocation.getArgument(0); request.getCallback().onSuccess(null); return null; - }).when(telemetryServiceMock).save(any(TimeseriesSaveRequest.class)); + }).when(telemetryServiceMock).saveTimeseries(any(TimeseriesSaveRequest.class)); node.onMsg(ctxMock, msg); List expectedList = getTsKvEntriesListWithTs(data, ts); - verify(telemetryServiceMock).save(assertArg((ThrowingConsumer) request -> { + verify(telemetryServiceMock).saveTimeseries(assertArg(request -> { assertThat(request.getTenantId()).isEqualTo(TENANT_ID); assertThat(request.getCustomerId()).isNull(); assertThat(request.getEntityId()).isEqualTo(DEVICE_ID); @@ -209,7 +209,7 @@ public class TbMsgTimeseriesNodeTest { TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metadata, data); node.onMsg(ctxMock, msg); - verify(telemetryServiceMock).save(assertArg((ThrowingConsumer) request -> { + verify(telemetryServiceMock).saveTimeseries(assertArg(request -> { assertThat(request.getTenantId()).isEqualTo(TENANT_ID); assertThat(request.getCustomerId()).isNull(); assertThat(request.getEntityId()).isEqualTo(DEVICE_ID); From 823d89dca6312f403a25b0f9b13f799dd1012d87 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 18 Dec 2024 17:34:35 +0200 Subject: [PATCH 017/108] Refactor delete for latest and history --- .../controller/TelemetryController.java | 31 ++++--- .../DefaultTbEntityViewService.java | 67 ++++++--------- .../DefaultTelemetrySubscriptionService.java | 64 ++++++-------- .../telemetry/InternalTelemetryService.java | 10 +-- .../dao/timeseries/TimeseriesService.java | 2 +- .../dao/timeseries/BaseTimeseriesService.java | 4 +- .../api/RuleEngineTelemetryService.java | 16 +--- .../engine/api/TimeseriesDeleteRequest.java | 85 +++++++++++++++++++ 8 files changed, 161 insertions(+), 118 deletions(-) create mode 100644 rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesDeleteRequest.java diff --git a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java index a518eaa1af..fa69c99245 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java @@ -49,6 +49,7 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.rule.engine.api.AttributesDeleteRequest; import org.thingsboard.rule.engine.api.AttributesSaveRequest; +import org.thingsboard.rule.engine.api.TimeseriesDeleteRequest; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.data.AttributeScope; @@ -521,19 +522,25 @@ public class TelemetryController extends BaseController { for (String key : keys) { deleteTsKvQueries.add(new BaseDeleteTsKvQuery(key, deleteFromTs, deleteToTs, rewriteLatestIfDeleted, deleteLatest)); } - tsSubService.deleteTimeseriesAndNotify(tenantId, entityId, keys, deleteTsKvQueries, new FutureCallback<>() { - @Override - public void onSuccess(@Nullable Void tmp) { - logTimeseriesDeleted(user, entityId, keys, deleteFromTs, deleteToTs, null); - result.setResult(new ResponseEntity<>(HttpStatus.OK)); - } + tsSubService.deleteTimeseries(TimeseriesDeleteRequest.builder() + .tenantId(tenantId) + .entityId(entityId) + .keys(keys) + .deleteHistoryQueries(deleteTsKvQueries) + .callback(new FutureCallback<>() { + @Override + public void onSuccess(@Nullable List tmp) { + logTimeseriesDeleted(user, entityId, keys, deleteFromTs, deleteToTs, null); + result.setResult(new ResponseEntity<>(HttpStatus.OK)); + } - @Override - public void onFailure(Throwable t) { - logTimeseriesDeleted(user, entityId, keys, deleteFromTs, deleteToTs, t); - result.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); - } - }); + @Override + public void onFailure(Throwable t) { + logTimeseriesDeleted(user, entityId, keys, deleteFromTs, deleteToTs, t); + result.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); + } + }) + .build()); }); } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java index 7dffb43cea..686e85cb1e 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java @@ -27,6 +27,7 @@ import org.springframework.stereotype.Service; import org.springframework.util.ConcurrentReferenceHashMap; import org.thingsboard.rule.engine.api.AttributesDeleteRequest; import org.thingsboard.rule.engine.api.AttributesSaveRequest; +import org.thingsboard.rule.engine.api.TimeseriesDeleteRequest; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.Customer; @@ -58,6 +59,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; @@ -403,51 +405,32 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen private ListenableFuture deleteLatestFromEntityView(EntityView entityView, List keys, User user) { EntityViewId entityId = entityView.getId(); SettableFuture resultFuture = SettableFuture.create(); - if (keys != null && !keys.isEmpty()) { - tsSubService.deleteLatest(entityView.getTenantId(), entityId, keys, new FutureCallback() { - @Override - public void onSuccess(@Nullable Void tmp) { - try { - logTimeseriesDeleted(entityView.getTenantId(), user, entityId, keys, null); - } catch (ThingsboardException e) { - log.error("Failed to log timeseries delete", e); - } - resultFuture.set(tmp); - } - - @Override - public void onFailure(Throwable t) { - try { - logTimeseriesDeleted(entityView.getTenantId(), user, entityId, keys, t); - } catch (ThingsboardException e) { - log.error("Failed to log timeseries delete", e); - } - resultFuture.setException(t); - } - }); - } else { - tsSubService.deleteAllLatest(entityView.getTenantId(), entityId, new FutureCallback>() { - @Override - public void onSuccess(@Nullable Collection keys) { - try { - logTimeseriesDeleted(entityView.getTenantId(), user, entityId, new ArrayList<>(keys), null); - } catch (ThingsboardException e) { - log.error("Failed to log timeseries delete", e); + tsSubService.deleteTimeseries(TimeseriesDeleteRequest.builder() + .tenantId(entityView.getTenantId()) + .entityId(entityId) + .keys(keys) + .callback(new FutureCallback<>() { + @Override + public void onSuccess(@Nullable List result) { + try { + logTimeseriesDeleted(entityView.getTenantId(), user, entityId, result, null); + } catch (ThingsboardException e) { + log.error("Failed to log timeseries delete", e); + } + resultFuture.set(null); } - resultFuture.set(null); - } - @Override - public void onFailure(Throwable t) { - try { - logTimeseriesDeleted(entityView.getTenantId(), user, entityId, Collections.emptyList(), t); - } catch (ThingsboardException e) { - log.error("Failed to log timeseries delete", e); + @Override + public void onFailure(Throwable t) { + try { + logTimeseriesDeleted(entityView.getTenantId(), user, entityId, Optional.ofNullable(keys).orElse(Collections.emptyList()), t); + } catch (ThingsboardException e) { + log.error("Failed to log timeseries delete", e); + } + resultFuture.setException(t); } - resultFuture.setException(t); - } - }); - } + }) + .build()); return resultFuture; } diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java index ad7f963894..d4444d5d7f 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java @@ -23,13 +23,16 @@ import jakarta.annotation.Nullable; import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; +import org.thingsboard.common.util.DonAsynchron; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.rule.engine.api.AttributesDeleteRequest; import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; +import org.thingsboard.rule.engine.api.TimeseriesDeleteRequest; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.EntityType; @@ -38,7 +41,6 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvLatestRemovingResult; import org.thingsboard.server.common.msg.queue.TbCallback; @@ -51,7 +53,6 @@ import org.thingsboard.server.service.entitiy.entityview.TbEntityViewService; import org.thingsboard.server.service.subscription.TbSubscriptionUtils; import java.util.ArrayList; -import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.List; @@ -60,6 +61,7 @@ import java.util.Objects; import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.function.Consumer; /** * Created by ashvayka on 27.03.18. @@ -177,38 +179,26 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer } @Override - public void deleteLatest(TenantId tenantId, EntityId entityId, List keys, FutureCallback callback) { - checkInternalEntity(entityId); - deleteLatestInternal(tenantId, entityId, keys, callback); - } - - @Override - public void deleteLatestInternal(TenantId tenantId, EntityId entityId, List keys, FutureCallback callback) { - ListenableFuture> deleteFuture = tsService.removeLatest(tenantId, entityId, keys); - addMainCallback(deleteFuture, callback); + public void deleteTimeseries(TimeseriesDeleteRequest request) { + checkInternalEntity(request.getEntityId()); + deleteTimeseriesInternal(request); } @Override - public void deleteAllLatest(TenantId tenantId, EntityId entityId, FutureCallback> callback) { - ListenableFuture> deleteFuture = tsService.removeAllLatest(tenantId, entityId); - Futures.addCallback(deleteFuture, new FutureCallback>() { - @Override - public void onSuccess(@Nullable Collection result) { - callback.onSuccess(result); + public void deleteTimeseriesInternal(TimeseriesDeleteRequest request) { + if (CollectionUtils.isNotEmpty(request.getKeys())) { + ListenableFuture> deleteFuture; + if (request.getDeleteHistoryQueries() == null) { + deleteFuture = tsService.removeLatest(request.getTenantId(), request.getEntityId(), request.getKeys()); + } else { + deleteFuture = tsService.remove(request.getTenantId(), request.getEntityId(), request.getDeleteHistoryQueries()); + addWsCallback(deleteFuture, result -> onTimeSeriesDelete(request.getTenantId(), request.getEntityId(), request.getKeys(), result)); } - - @Override - public void onFailure(Throwable t) { - callback.onFailure(t); - } - }, tsCallBackExecutor); - } - - @Override - public void deleteTimeseriesAndNotify(TenantId tenantId, EntityId entityId, List keys, List deleteTsKvQueries, FutureCallback callback) { - ListenableFuture> deleteFuture = tsService.remove(tenantId, entityId, deleteTsKvQueries); - addMainCallback(deleteFuture, callback); - addWsCallback(deleteFuture, list -> onTimeSeriesDelete(tenantId, entityId, keys, list)); + addMainCallback(deleteFuture, __ -> request.getCallback().onSuccess(request.getKeys()), request.getCallback()::onFailure); + } else { + ListenableFuture> deleteFuture = tsService.removeAllLatest(request.getTenantId(), request.getEntityId()); + addMainCallback(deleteFuture, request.getCallback()::onSuccess, request.getCallback()::onFailure); + } } private void addEntityViewCallback(TenantId tenantId, EntityId entityId, List ts) { @@ -314,17 +304,11 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer private void addMainCallback(ListenableFuture saveFuture, final FutureCallback callback) { if (callback == null) return; - Futures.addCallback(saveFuture, new FutureCallback() { - @Override - public void onSuccess(@Nullable S result) { - callback.onSuccess(null); - } + addMainCallback(saveFuture, result -> callback.onSuccess(null), callback::onFailure); + } - @Override - public void onFailure(Throwable t) { - callback.onFailure(t); - } - }, tsCallBackExecutor); + private void addMainCallback(ListenableFuture saveFuture, Consumer onSuccess, Consumer onFailure) { + DonAsynchron.withCallback(saveFuture, onSuccess, onFailure, tsCallBackExecutor); } private void checkInternalEntity(EntityId entityId) { diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java index 1db6a86507..8e45b84a75 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java @@ -15,16 +15,12 @@ */ package org.thingsboard.server.service.telemetry; -import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.rule.engine.api.AttributesDeleteRequest; import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; +import org.thingsboard.rule.engine.api.TimeseriesDeleteRequest; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.TenantId; - -import java.util.List; /** * Created by ashvayka on 27.03.18. @@ -35,8 +31,8 @@ public interface InternalTelemetryService extends RuleEngineTelemetryService { void saveAttributesInternal(AttributesSaveRequest request); - void deleteAttributesInternal(AttributesDeleteRequest request); + void deleteTimeseriesInternal(TimeseriesDeleteRequest request); - void deleteLatestInternal(TenantId tenantId, EntityId entityId, List keys, FutureCallback callback); + void deleteAttributesInternal(AttributesDeleteRequest request); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesService.java index eaba144042..49918f3823 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesService.java @@ -56,7 +56,7 @@ public interface TimeseriesService { ListenableFuture> removeLatest(TenantId tenantId, EntityId entityId, Collection keys); - ListenableFuture> removeAllLatest(TenantId tenantId, EntityId entityId); + ListenableFuture> removeAllLatest(TenantId tenantId, EntityId entityId); List findAllKeysByDeviceProfileId(TenantId tenantId, DeviceProfileId deviceProfileId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java index f9c4218d64..756b73d88b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java @@ -254,11 +254,11 @@ public class BaseTimeseriesService implements TimeseriesService { } @Override - public ListenableFuture> removeAllLatest(TenantId tenantId, EntityId entityId) { + public ListenableFuture> removeAllLatest(TenantId tenantId, EntityId entityId) { validate(entityId); return Futures.transformAsync(this.findAllLatest(tenantId, entityId), latest -> { if (latest != null && !latest.isEmpty()) { - Collection keys = latest.stream().map(TsKvEntry::getKey).collect(Collectors.toList()); + List keys = latest.stream().map(TsKvEntry::getKey).collect(Collectors.toList()); return Futures.transform(this.removeLatest(tenantId, entityId, keys), res -> keys, MoreExecutors.directExecutor()); } else { return Futures.immediateFuture(Collections.emptyList()); diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java index 17ed6aaa13..17696b4bb1 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java @@ -15,14 +15,6 @@ */ package org.thingsboard.rule.engine.api; -import com.google.common.util.concurrent.FutureCallback; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; - -import java.util.Collection; -import java.util.List; - /** * Created by ashvayka on 02.04.18. */ @@ -32,12 +24,8 @@ public interface RuleEngineTelemetryService { void saveAttributes(AttributesSaveRequest request); - void deleteAttributes(AttributesDeleteRequest request); - - void deleteLatest(TenantId tenantId, EntityId entityId, List keys, FutureCallback callback); - - void deleteAllLatest(TenantId tenantId, EntityId entityId, FutureCallback> callback); + void deleteTimeseries(TimeseriesDeleteRequest request); - void deleteTimeseriesAndNotify(TenantId tenantId, EntityId entityId, List keys, List deleteTsKvQueries, FutureCallback callback); + void deleteAttributes(AttributesDeleteRequest request); } diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesDeleteRequest.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesDeleteRequest.java new file mode 100644 index 0000000000..b124806fff --- /dev/null +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesDeleteRequest.java @@ -0,0 +1,85 @@ +/** + * 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. + */ +package org.thingsboard.rule.engine.api; + +import com.google.common.util.concurrent.FutureCallback; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; + +import java.util.List; + +@Getter +@ToString +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class TimeseriesDeleteRequest { + + private final TenantId tenantId; + private final EntityId entityId; + private final List keys; + private final List deleteHistoryQueries; + private final FutureCallback> callback; + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private TenantId tenantId; + private EntityId entityId; + private List keys; + private List deleteHistoryQueries; + private FutureCallback> callback; + + Builder() {} + + public Builder tenantId(TenantId tenantId) { + this.tenantId = tenantId; + return this; + } + + public Builder entityId(EntityId entityId) { + this.entityId = entityId; + return this; + } + + public Builder keys(List keys) { + this.keys = keys; + return this; + } + + public Builder deleteHistoryQueries(List deleteHistoryQueries) { + this.deleteHistoryQueries = deleteHistoryQueries; + return this; + } + + public Builder callback(FutureCallback> callback) { + this.callback = callback; + return this; + } + + public TimeseriesDeleteRequest build() { + return new TimeseriesDeleteRequest(tenantId, entityId, keys, deleteHistoryQueries, callback); + } + + } + +} From ac61d7c5262ed88c29f5f845f77b4cd7968183d7 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 19 Dec 2024 16:09:36 +0200 Subject: [PATCH 018/108] TbMsg: add resetRuleNodeId and copyMetaData --- .../actors/ruleChain/DefaultTbContext.java | 12 +- .../RuleChainActorMessageProcessor.java | 4 +- .../server/controller/RpcV2Controller.java | 2 +- .../controller/RuleChainController.java | 3 +- .../controller/RuleEngineController.java | 2 +- .../service/action/EntityActionService.java | 2 +- .../device/DeviceProvisionServiceImpl.java | 4 +- .../service/edge/rpc/EdgeGrpcService.java | 2 +- .../edge/rpc/processor/BaseEdgeProcessor.java | 2 +- .../processor/device/DeviceEdgeProcessor.java | 2 +- .../telemetry/BaseTelemetryProcessor.java | 9 +- .../entitiy/EntityStateSourcingListener.java | 2 +- .../rpc/DefaultTbCoreDeviceRpcService.java | 2 +- .../server/service/rpc/TbRpcService.java | 2 +- .../state/DefaultDeviceStateService.java | 2 +- .../transport/DefaultTransportApiService.java | 2 +- .../actors/rule/DefaultTbContextTest.java | 6 +- .../controller/RuleEngineControllerTest.java | 10 +- .../controller/TenantControllerTest.java | 2 +- ...AbstractRuleEngineFlowIntegrationTest.java | 4 +- ...actRuleEngineLifecycleIntegrationTest.java | 2 +- .../queue/DefaultTbClusterServiceTest.java | 8 +- .../TbRuleEngineQueueConsumerManagerTest.java | 2 +- .../DefaultTbRuleEngineRpcServiceTest.java | 2 +- .../DefaultRuleEngineCallServiceTest.java | 4 +- .../SequentialTimeseriesPersistenceTest.java | 2 +- .../thingsboard/server/common/msg/TbMsg.java | 11 +- .../service/DefaultTransportService.java | 3 +- .../rule/engine/api/util/TbNodeUtilsTest.java | 10 +- .../rule/engine/action/TbMsgCountNode.java | 2 +- .../deduplication/TbMsgDeduplicationNode.java | 4 +- .../rule/engine/delay/TbMsgDelayNode.java | 2 +- .../engine/profile/TbDeviceProfileNode.java | 6 +- .../action/TbAssignToCustomerNodeTest.java | 2 +- .../engine/action/TbClearAlarmNodeTest.java | 4 +- .../TbCopyAttributesToEntityViewNodeTest.java | 12 +- .../engine/action/TbCreateAlarmNodeTest.java | 14 +- .../action/TbCreateRelationNodeTest.java | 4 +- .../action/TbDeleteRelationNodeTest.java | 2 +- .../engine/action/TbDeviceStateNodeTest.java | 6 +- .../rule/engine/action/TbLogNodeTest.java | 6 +- .../engine/action/TbMsgCountNodeTest.java | 6 +- .../TbSaveToCustomCassandraTableNodeTest.java | 10 +- .../TbUnassignFromCustomerNodeTest.java | 2 +- .../aws/lambda/TbAwsLambdaNodeTest.java | 10 +- .../rule/engine/aws/sns/TbSnsNodeTest.java | 4 +- .../rule/engine/aws/sqs/TbSqsNodeTest.java | 8 +- .../engine/debug/TbMsgGeneratorNodeTest.java | 8 +- .../engine/edge/TbMsgPushToEdgeNodeTest.java | 12 +- .../filter/TbAssetTypeSwitchNodeTest.java | 2 +- .../filter/TbCheckAlarmStatusNodeTest.java | 2 +- .../engine/filter/TbCheckMessageNodeTest.java | 4 +- .../filter/TbCheckRelationNodeTest.java | 2 +- .../filter/TbDeviceTypeSwitchNodeTest.java | 2 +- .../engine/filter/TbJsFilterNodeTest.java | 6 +- .../engine/filter/TbJsSwitchNodeTest.java | 2 +- .../filter/TbMsgTypeFilterNodeTest.java | 2 +- .../filter/TbMsgTypeSwitchNodeTest.java | 2 +- .../TbOriginatorTypeFilterNodeTest.java | 2 +- .../TbOriginatorTypeSwitchNodeTest.java | 2 +- .../rule/engine/flow/TbAckNodeTest.java | 2 +- .../engine/flow/TbCheckpointNodeTest.java | 4 +- .../engine/flow/TbRuleChainInputNodeTest.java | 2 +- .../flow/TbRuleChainOutputNodeTest.java | 2 +- .../engine/gcp/pubsub/TbPubSubNodeTest.java | 8 +- .../geo/TbGpsGeofencingActionNodeTest.java | 2 +- .../geo/TbGpsGeofencingFilterNodeTest.java | 4 +- .../rule/engine/kafka/TbKafkaNodeTest.java | 12 +- .../engine/mail/TbMsgToEmailNodeTest.java | 2 +- .../rule/engine/math/TbMathNodeTest.java | 38 ++--- .../metadata/CalculateDeltaNodeTest.java | 32 ++-- .../TbFetchDeviceCredentialsNodeTest.java | 2 +- .../metadata/TbGetAttributesNodeTest.java | 4 +- .../TbGetCustomerAttributeNodeTest.java | 6 +- .../TbGetCustomerDetailsNodeTest.java | 4 +- .../metadata/TbGetDeviceAttrNodeTest.java | 2 +- .../TbGetOriginatorFieldsNodeTest.java | 12 +- .../TbGetRelatedAttributeNodeTest.java | 4 +- .../metadata/TbGetTelemetryNodeTest.java | 20 +-- .../TbGetTenantAttributeNodeTest.java | 4 +- .../metadata/TbGetTenantDetailsNodeTest.java | 4 +- .../rule/engine/mqtt/TbMqttNodeTest.java | 4 +- .../rule/engine/profile/DeviceStateTest.java | 15 +- .../profile/TbDeviceProfileNodeTest.java | 143 ++++++------------ .../engine/rabbitmq/TbRabbitMqNodeTest.java | 6 +- .../rule/engine/rest/TbHttpClientTest.java | 4 +- .../engine/rest/TbRestApiCallNodeTest.java | 4 +- .../rest/TbSendRestApiCallReplyNodeTest.java | 4 +- .../engine/rpc/TbSendRPCReplyNodeTest.java | 12 +- .../engine/rpc/TbSendRPCRequestNodeTest.java | 40 ++--- .../telemetry/TbMsgAttributesNodeTest.java | 2 +- .../TbMsgDeleteAttributesNodeTest.java | 2 +- .../telemetry/TbMsgTimeseriesNodeTest.java | 8 +- .../transform/TbChangeOriginatorNodeTest.java | 12 +- .../engine/transform/TbCopyKeysNodeTest.java | 2 +- .../transform/TbDeleteKeysNodeTest.java | 2 +- .../engine/transform/TbJsonPathNodeTest.java | 2 +- .../transform/TbMsgDeduplicationNodeTest.java | 4 +- .../transform/TbRenameKeysNodeTest.java | 2 +- .../transform/TbSplitArrayMsgNodeTest.java | 2 +- .../transform/TbTransformMsgNodeTest.java | 6 +- 101 files changed, 330 insertions(+), 388 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index eed7093cd0..c3c524b2bb 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -175,7 +175,7 @@ public class DefaultTbContext implements TbContext { } TbMsg tbMsg = msg.copy() .ruleChainId(ruleChainId) - .ruleNodeId(null) + .resetRuleNodeId() .build(); tbMsg.pushToStack(nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId()); TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getQueueName(), getTenantId(), tbMsg.getOriginator()); @@ -371,7 +371,7 @@ public class DefaultTbContext implements TbContext { .type(type) .originator(originator) .customerId(customerId) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(data) .ruleChainId(nodeCtx.getSelf().getRuleChainId()) .ruleNodeId(nodeCtx.getSelf().getId()) @@ -400,7 +400,7 @@ public class DefaultTbContext implements TbContext { .type(type) .originator(originator) .customerId(customerId) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(data) .ruleChainId(nodeCtx.getSelf().getRuleChainId()) .ruleNodeId(nodeCtx.getSelf().getId()) @@ -532,10 +532,9 @@ public class DefaultTbContext implements TbContext { .queueName(defaultQueueName) .type(action) .originator(id) - .metaData(msgMetaData.copy()) + .copyMetaData(msgMetaData) .data(msgData) .ruleChainId(defaultRuleChainId) - .ruleNodeId(null) .build(); } @@ -558,10 +557,9 @@ public class DefaultTbContext implements TbContext { .queueName(defaultQueueName) .type(actionMsgType) .originator(id) - .metaData(msgMetaData.copy()) + .copyMetaData(msgMetaData) .data(msgData) .ruleChainId(defaultRuleChainId) - .ruleNodeId(null) .build(); } diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java index a2a0ab8c80..be0812795d 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java @@ -219,7 +219,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor node.onMsg(ctxMock, msg)) @@ -214,7 +214,7 @@ public class TbSaveToCustomCassandraTableNodeTest extends AbstractRuleNodeUpgrad TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(data) .build(); assertThatThrownBy(() -> node.onMsg(ctxMock, msg)) @@ -240,7 +240,7 @@ public class TbSaveToCustomCassandraTableNodeTest extends AbstractRuleNodeUpgrad TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(data) .build(); assertThatThrownBy(() -> node.onMsg(ctxMock, msg)) @@ -267,7 +267,7 @@ public class TbSaveToCustomCassandraTableNodeTest extends AbstractRuleNodeUpgrad TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); node.onMsg(ctxMock, msg); @@ -322,7 +322,7 @@ public class TbSaveToCustomCassandraTableNodeTest extends AbstractRuleNodeUpgrad TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(data) .build(); node.onMsg(ctxMock, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNodeTest.java index 971a858758..d7858e5ceb 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNodeTest.java @@ -297,7 +297,7 @@ class TbUnassignFromCustomerNodeTest extends AbstractRuleNodeUpgradeTest { return TbMsg.newMsg() .type(TbMsgType.NA) .originator(originator) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNodeTest.java index b05fc3520c..0afa3804b9 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNodeTest.java @@ -148,7 +148,7 @@ public class TbAwsLambdaNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(metadata.copy()) + .copyMetaData(metadata) .data(data) .build(); @@ -208,7 +208,7 @@ public class TbAwsLambdaNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_ARRAY) .build(); InvokeRequest request = createInvokeRequest(msg); @@ -251,7 +251,7 @@ public class TbAwsLambdaNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); InvokeRequest request = createInvokeRequest(msg); @@ -292,7 +292,7 @@ public class TbAwsLambdaNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); InvokeRequest request = createInvokeRequest(msg); @@ -333,7 +333,7 @@ public class TbAwsLambdaNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); InvokeRequest request = createInvokeRequest(msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sns/TbSnsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sns/TbSnsNodeTest.java index 8fc649d10c..e085857f3d 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sns/TbSnsNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sns/TbSnsNodeTest.java @@ -108,7 +108,7 @@ class TbSnsNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(data) .build(); node.onMsg(ctxMock, msg); @@ -151,7 +151,7 @@ class TbSnsNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); node.onMsg(ctxMock, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNodeTest.java index 60785d409e..3da7f3d190 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNodeTest.java @@ -110,7 +110,7 @@ class TbSqsNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(data) .build(); node.onMsg(ctxMock, msg); @@ -151,7 +151,7 @@ class TbSqsNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(data) .build(); node.onMsg(ctxMock, msg); @@ -199,7 +199,7 @@ class TbSqsNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); node.onMsg(ctxMock, msg); @@ -239,7 +239,7 @@ class TbSqsNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); node.onMsg(ctxMock, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNodeTest.java index 5d40328ae4..cd5498c5d8 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNodeTest.java @@ -193,7 +193,7 @@ public class TbMsgGeneratorNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg tickMsg = TbMsg.newMsg() .type(TbMsgType.GENERATOR_NODE_SELF_MSG) .originator(RULE_NODE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_STRING) .build(); given(ctxMock.newMsg(null, TbMsgType.GENERATOR_NODE_SELF_MSG, RULE_NODE_ID, null, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING)).willReturn(tickMsg); @@ -211,7 +211,7 @@ public class TbMsgGeneratorNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg firstMsg = TbMsg.newMsg() .type(TbMsg.EMPTY_STRING) .originator(RULE_NODE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); given(ctxMock.newMsg(null, TbMsg.EMPTY_STRING, RULE_NODE_ID, null, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT)).willReturn(firstMsg); @@ -221,7 +221,7 @@ public class TbMsgGeneratorNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg generatedMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(RULE_NODE_ID) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data("{ \"temp\": 42, \"humidity\": 77 }") .build(); given(scriptEngineMock.executeGenerateAsync(any())).willReturn(Futures.immediateFuture(generatedMsg)); @@ -230,7 +230,7 @@ public class TbMsgGeneratorNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg prevMsg = TbMsg.newMsg() .type(generatedMsg.getType()) .originator(RULE_NODE_ID) - .metaData(generatedMsg.getMetaData().copy()) + .copyMetaData(generatedMsg.getMetaData()) .data(generatedMsg.getData()) .build(); given(ctxMock.newMsg(null, generatedMsg.getType(), RULE_NODE_ID, null, generatedMsg.getMetaData(), generatedMsg.getData())).willReturn(prevMsg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNodeTest.java index e84850498d..602983398f 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNodeTest.java @@ -88,11 +88,9 @@ public class TbMsgPushToEdgeNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(TbMsg.EMPTY_JSON_OBJECT) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg); @@ -116,11 +114,9 @@ public class TbMsgPushToEdgeNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.ATTRIBUTES_UPDATED) .originator(userId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(TbMsg.EMPTY_JSON_OBJECT) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg); @@ -154,11 +150,9 @@ public class TbMsgPushToEdgeNodeTest { TbMsg msg = TbMsg.newMsg() .type(event) .originator(new EdgeId(UUID.randomUUID())) - .metaData(metaData.copy()) + .copyMetaData(metaData) .dataType(TbMsgDataType.JSON) .data("{\"lastConnectTs\":1}") - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNodeTest.java index eb99770537..15519e18ee 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNodeTest.java @@ -121,7 +121,7 @@ class TbAssetTypeSwitchNodeTest { return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .callback(callback) .build(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java index 012303278f..0068289080 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java @@ -177,7 +177,7 @@ class TbCheckAlarmStatusNodeTest { return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(msgData) .build(); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckMessageNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckMessageNodeTest.java index 377c0b69e7..9ecac1a271 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckMessageNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckMessageNodeTest.java @@ -47,7 +47,7 @@ class TbCheckMessageNodeTest { private static final TbMsg EMPTY_POST_ATTRIBUTES_MSG = TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); @@ -204,7 +204,7 @@ class TbCheckMessageNodeTest { return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(DEVICE_ID) - .metaData(metadata.copy()) + .copyMetaData(metadata) .data(data) .build(); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeTest.java index 9ddf70b9dc..72a78b73a0 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeTest.java @@ -69,7 +69,7 @@ class TbCheckRelationNodeTest extends AbstractRuleNodeUpgradeTest { private final TbMsg EMPTY_POST_ATTRIBUTES_MSG = TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(ORIGINATOR_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbDeviceTypeSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbDeviceTypeSwitchNodeTest.java index 64559c5125..31bddccb21 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbDeviceTypeSwitchNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbDeviceTypeSwitchNodeTest.java @@ -121,7 +121,7 @@ class TbDeviceTypeSwitchNodeTest { return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .callback(callback) .build(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java index e6d04d6af7..79bd3fa710 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java @@ -62,7 +62,7 @@ public class TbJsFilterNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(null) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(TbMsg.EMPTY_JSON_OBJECT) .ruleChainId(ruleChainId) @@ -82,7 +82,7 @@ public class TbJsFilterNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(null) - .metaData(metaData.copy()) + .copyMetaData(metaData) .dataType(TbMsgDataType.JSON) .data(TbMsg.EMPTY_JSON_OBJECT) .ruleChainId(ruleChainId) @@ -102,7 +102,7 @@ public class TbJsFilterNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(null) - .metaData(metaData.copy()) + .copyMetaData(metaData) .dataType(TbMsgDataType.JSON) .data(TbMsg.EMPTY_JSON_OBJECT) .ruleChainId(ruleChainId) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java index 981ce15a5f..74cd7e4f4f 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java @@ -62,7 +62,7 @@ public class TbJsSwitchNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(null) - .metaData(metaData.copy()) + .copyMetaData(metaData) .dataType(TbMsgDataType.JSON) .data(rawJson) .ruleChainId(ruleChainId) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNodeTest.java index 4a6b6830f5..2d9cf75e22 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNodeTest.java @@ -100,7 +100,7 @@ class TbMsgTypeFilterNodeTest { return TbMsg.newMsg() .type(msgType) .originator(entityId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNodeTest.java index 21f6d94829..1d8bb285af 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNodeTest.java @@ -92,7 +92,7 @@ class TbMsgTypeSwitchNodeTest { return TbMsg.newMsg() .type(msgType) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNodeTest.java index 3246535e15..378e5d53bf 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNodeTest.java @@ -99,7 +99,7 @@ class TbOriginatorTypeFilterNodeTest { return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNodeTest.java index acde991b6e..4782c1bdee 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNodeTest.java @@ -93,7 +93,7 @@ class TbOriginatorTypeSwitchNodeTest { return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbAckNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbAckNodeTest.java index dc166a775b..f712a607d0 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbAckNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbAckNodeTest.java @@ -70,7 +70,7 @@ public class TbAckNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); node.onMsg(ctxMock, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeTest.java index d8f5d3aa24..8c3c72fb2d 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeTest.java @@ -90,7 +90,7 @@ public class TbCheckpointNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); node.onMsg(ctxMock, msg); @@ -109,7 +109,7 @@ public class TbCheckpointNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); node.onMsg(ctxMock, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainInputNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainInputNodeTest.java index f294f7b7da..4457b3c671 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainInputNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainInputNodeTest.java @@ -303,7 +303,7 @@ public class TbRuleChainInputNodeTest extends AbstractRuleNodeUpgradeTest { return TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(entityId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_STRING) .build(); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainOutputNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainOutputNodeTest.java index aa6e9aeb6f..49d24f579f 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainOutputNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainOutputNodeTest.java @@ -77,7 +77,7 @@ public class TbRuleChainOutputNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); node.onMsg(ctxMock, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNodeTest.java index dcf087af13..49fc03f784 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNodeTest.java @@ -121,7 +121,7 @@ class TbPubSubNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(data) .build(); node.onMsg(ctxMock, msg); @@ -174,7 +174,7 @@ class TbPubSubNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(metadata.copy()) + .copyMetaData(metadata) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); node.onMsg(ctxMock, msg); @@ -210,7 +210,7 @@ class TbPubSubNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); node.onMsg(ctxMock, msg); @@ -245,7 +245,7 @@ class TbPubSubNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); node.onMsg(ctxMock, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeTest.java index 997c9dd7fd..872225243b 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeTest.java @@ -167,7 +167,7 @@ class TbGpsGeofencingActionNodeTest extends AbstractRuleNodeUpgradeTest { return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) - .metaData(metadata.copy()) + .copyMetaData(metadata) .data(data) .build(); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeTest.java index 1e3878e015..9a7a79b9ad 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeTest.java @@ -451,7 +451,7 @@ class TbGpsGeofencingFilterNodeTest { return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) - .metaData(metadata.copy()) + .copyMetaData(metadata) .data(data) .build(); } @@ -460,7 +460,7 @@ class TbGpsGeofencingFilterNodeTest { return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_ARRAY) .build(); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeTest.java index 4713f0cfd9..1d7635476e 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeTest.java @@ -191,7 +191,7 @@ public class TbKafkaNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); node.onMsg(ctxMock, msg); @@ -220,7 +220,7 @@ public class TbKafkaNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); node.onMsg(ctxMock, msg); @@ -245,7 +245,7 @@ public class TbKafkaNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(data) .build(); String topic = TbNodeUtils.processPattern(topicPattern, msg); @@ -296,7 +296,7 @@ public class TbKafkaNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); node.onMsg(ctxMock, msg); @@ -326,7 +326,7 @@ public class TbKafkaNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); node.onMsg(ctxMock, msg); @@ -359,7 +359,7 @@ public class TbKafkaNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); node.onMsg(ctxMock, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeTest.java index eaa0d00a25..86d90ec9c0 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeTest.java @@ -106,7 +106,7 @@ public class TbMsgToEmailNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) - .metaData(md.copy()) + .copyMetaData(md) .data(msgDataStr) .build(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java index 1a8e8179a9..ae313822d7 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java @@ -176,7 +176,7 @@ public class TbMathNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(msgNode.toString()) .build(); @@ -189,7 +189,7 @@ public class TbMathNodeTest { msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(msgNode.toString()) .build(); @@ -241,7 +241,7 @@ public class TbMathNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(JacksonUtil.newObjectNode().put("a", arg1).put("b", arg2).toString()) .build(); @@ -309,7 +309,7 @@ public class TbMathNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(JacksonUtil.newObjectNode().put("a", arg1).toString()) .build(); @@ -337,7 +337,7 @@ public class TbMathNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString()) .build(); @@ -365,7 +365,7 @@ public class TbMathNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString()) .build(); @@ -394,7 +394,7 @@ public class TbMathNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(JacksonUtil.newObjectNode().toString()) .build(); @@ -427,7 +427,7 @@ public class TbMathNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(JacksonUtil.newObjectNode().put("a", 5).toString()) .build(); @@ -454,7 +454,7 @@ public class TbMathNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(JacksonUtil.newObjectNode().put("a", 5).toString()) .build(); @@ -481,7 +481,7 @@ public class TbMathNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(JacksonUtil.newObjectNode().put("a", 5).toString()) .build(); @@ -512,7 +512,7 @@ public class TbMathNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(JacksonUtil.newObjectNode().put("a", 5).toString()) .build(); when(telemetryService.saveAndNotify(any(), any(), any(TsKvEntry.class))) @@ -542,7 +542,7 @@ public class TbMathNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(JacksonUtil.newObjectNode().put("a", 5).toString()) .build(); when(telemetryService.saveAndNotify(any(), any(), any(TsKvEntry.class))) @@ -578,7 +578,7 @@ public class TbMathNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(JacksonUtil.newObjectNode().put("a", 10).toString()) .build(); @@ -603,7 +603,7 @@ public class TbMathNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(JacksonUtil.newObjectNode().put("a", 10).toString()) .build(); node.onMsg(ctx, msg); @@ -623,7 +623,7 @@ public class TbMathNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_ARRAY) .build(); node.onMsg(ctx, msg); @@ -648,7 +648,7 @@ public class TbMathNodeTest { .mapToObj(x -> TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originatorSlow) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString()) .build()) .toList(); @@ -656,7 +656,7 @@ public class TbMathNodeTest { .mapToObj(x -> TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originatorFast) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString()) .build()) .toList(); @@ -728,7 +728,7 @@ public class TbMathNodeTest { .mapToObj(x -> TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originatorSlow) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString()) .build()) .collect(Collectors.toList()); @@ -806,7 +806,7 @@ public class TbMathNodeTest { .onMsg(ctxNode.getLeft(), TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data("{\"a\":2,\"b\":2}") .build()))); ctxNodes.forEach(ctxNode -> verify(ctxNode.getRight(), timeout(TIMEOUT)).onMsg(eq(ctxNode.getLeft()), any())); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeTest.java index bfb2b46037..823b02fe59 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeTest.java @@ -172,7 +172,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { var msg = TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(msgData) .build(); @@ -192,7 +192,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_ARRAY) .build(); @@ -213,7 +213,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); @@ -242,7 +242,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(msgData) .build(); @@ -277,7 +277,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(msgData) .build(); @@ -312,7 +312,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(msgData) .build(); @@ -351,7 +351,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { var firstMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(firstMsgMetaData.copy()) + .copyMetaData(firstMsgMetaData) .data(msgData) .build(); @@ -382,7 +382,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { var secondMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(secondMsgMetaData.copy()) + .copyMetaData(secondMsgMetaData) .data(msgData) .build(); @@ -418,7 +418,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(msgData) .build(); @@ -451,7 +451,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(msgData) .build(); @@ -488,7 +488,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(msgData) .build(); @@ -517,7 +517,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(msgData) .build(); @@ -547,7 +547,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(msgData) .build(); @@ -577,7 +577,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(msgData) .build(); @@ -625,7 +625,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { return TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(msgData) .build(); }).toList(); @@ -675,7 +675,7 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest { var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(msgData) .build(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNodeTest.java index c11dcfd749..ecb9ab80ee 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNodeTest.java @@ -176,7 +176,7 @@ public class TbFetchDeviceCredentialsNodeTest { return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(data) .callback(callbackMock) .build(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNodeTest.java index 1f5770bf52..22144ff32e 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNodeTest.java @@ -249,7 +249,7 @@ public class TbGetAttributesNodeTest extends AbstractRuleNodeUpgradeTest { var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(ORIGINATOR_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_ARRAY) .build(); @@ -350,7 +350,7 @@ public class TbGetAttributesNodeTest extends AbstractRuleNodeUpgradeTest { return TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(entityId) - .metaData(msgMetaData.copy()) + .copyMetaData(msgMetaData) .data(JacksonUtil.toString(msgData)) .build(); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java index c5ce45a996..e8e0b42a76 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java @@ -213,7 +213,7 @@ public class TbGetCustomerAttributeNodeTest { msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_ARRAY) .build(); @@ -233,7 +233,7 @@ public class TbGetCustomerAttributeNodeTest { msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(userId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); @@ -482,7 +482,7 @@ public class TbGetCustomerAttributeNodeTest { msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) - .metaData(msgMetaData.copy()) + .copyMetaData(msgMetaData) .data(msgData) .build(); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeTest.java index 31cb6a7083..47c78bc106 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeTest.java @@ -162,7 +162,7 @@ public class TbGetCustomerDetailsNodeTest { msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_ARRAY) .build(); @@ -466,7 +466,7 @@ public class TbGetCustomerDetailsNodeTest { msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) - .metaData(msgMetaData.copy()) + .copyMetaData(msgMetaData) .data(msgData) .build(); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNodeTest.java index fb21fe3737..212fa7993c 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNodeTest.java @@ -118,7 +118,7 @@ public class TbGetDeviceAttrNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); node.onMsg(ctxMock, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNodeTest.java index 75a0bf4868..54da7e6e78 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNodeTest.java @@ -138,7 +138,7 @@ public class TbGetOriginatorFieldsNodeTest { msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_ARRAY) .build(); @@ -172,7 +172,7 @@ public class TbGetOriginatorFieldsNodeTest { msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(msgMetaData.copy()) + .copyMetaData(msgMetaData) .data(msgData) .build(); @@ -219,7 +219,7 @@ public class TbGetOriginatorFieldsNodeTest { msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(msgMetaData.copy()) + .copyMetaData(msgMetaData) .data(msgData) .build(); @@ -267,7 +267,7 @@ public class TbGetOriginatorFieldsNodeTest { msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(msgMetaData.copy()) + .copyMetaData(msgMetaData) .data(msgData) .build(); @@ -320,7 +320,7 @@ public class TbGetOriginatorFieldsNodeTest { msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(msgMetaData.copy()) + .copyMetaData(msgMetaData) .data(msgData) .build(); @@ -383,7 +383,7 @@ public class TbGetOriginatorFieldsNodeTest { msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(new DashboardId(UUID.randomUUID())) - .metaData(msgMetaData.copy()) + .copyMetaData(msgMetaData) .data(msgData) .build(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java index 83a9d4afbf..9ca0d64307 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java @@ -226,7 +226,7 @@ public class TbGetRelatedAttributeNodeTest { msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_ARRAY) .build(); @@ -600,7 +600,7 @@ public class TbGetRelatedAttributeNodeTest { msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) - .metaData(msgMetaData.copy()) + .copyMetaData(msgMetaData) .data(msgData) .build(); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNodeTest.java index 69d1200746..f2f4f572dc 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNodeTest.java @@ -185,7 +185,7 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); assertThatThrownBy(() -> node.onMsg(ctxMock, msg)) @@ -213,7 +213,7 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data("{\"msgEndInterval\":\"" + endTs + "\"}") .build(); node.onMsg(ctxMock, msg); @@ -240,7 +240,7 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); node.onMsg(ctxMock, msg); @@ -269,7 +269,7 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data("{\"msgTsKey\":\"pressure\"}") .build(); node.onMsg(ctxMock, msg); @@ -299,7 +299,7 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); node.onMsg(ctxMock, msg); @@ -334,7 +334,7 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); node.onMsg(ctxMock, msg); @@ -379,7 +379,7 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); node.onMsg(ctxMock, msg); @@ -420,7 +420,7 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data("{\"msgStartInterval\":\"start\"}") .build(); assertThatThrownBy(() -> node.onMsg(ctxMock, msg)).isInstanceOf(IllegalArgumentException.class).hasMessage(errorMsg); @@ -454,7 +454,7 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); node.onMsg(ctxMock, msg); @@ -492,7 +492,7 @@ public class TbGetTelemetryNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); node.onMsg(ctxMock, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java index 5ad0163a7c..7dd69e051e 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java @@ -193,7 +193,7 @@ public class TbGetTenantAttributeNodeTest { msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_ARRAY) .build(); @@ -406,7 +406,7 @@ public class TbGetTenantAttributeNodeTest { msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) - .metaData(msgMetaData.copy()) + .copyMetaData(msgMetaData) .data(msgData) .build(); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeTest.java index 7cf9ddcdc8..4dc37c824e 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeTest.java @@ -132,7 +132,7 @@ public class TbGetTenantDetailsNodeTest { msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_ARRAY) .build(); @@ -297,7 +297,7 @@ public class TbGetTenantDetailsNodeTest { msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DUMMY_DEVICE_ORIGINATOR) - .metaData(msgMetaData.copy()) + .copyMetaData(msgMetaData) .data(msgData) .build(); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeTest.java index aa6700dd20..0cd8308ded 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeTest.java @@ -286,7 +286,7 @@ public class TbMqttNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(data) .build(); mqttNode.onMsg(ctxMock, msg); @@ -330,7 +330,7 @@ public class TbMqttNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data("\"string\"") .build(); mqttNode.onMsg(ctxMock, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/DeviceStateTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/DeviceStateTest.java index 897a332c95..99cec83cfc 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/DeviceStateTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/DeviceStateTest.java @@ -97,8 +97,7 @@ public class DeviceStateTest { String data = invocationOnMock.getArgument(invocationOnMock.getArguments().length - 1); return TbMsg.newMsg() .type(type) - .originator(null) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(data) .build(); }); @@ -115,7 +114,7 @@ public class DeviceStateTest { TbMsg attributeUpdateMsg = TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data("{ \"enabled\": false }") .build(); @@ -128,7 +127,7 @@ public class DeviceStateTest { deviceState.process(ctx, TbMsg.newMsg() .type(TbMsgType.ALARM_CLEAR) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(JacksonUtil.toString(alarm)) .build()); reset(ctx); @@ -137,7 +136,7 @@ public class DeviceStateTest { deviceState.process(ctx, TbMsg.newMsg() .type(TbMsgType.ATTRIBUTES_DELETED) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(deletedAttributes) .build()); verify(ctx, never()).enqueueForTellNext(any(), anyString()); @@ -152,7 +151,7 @@ public class DeviceStateTest { TbMsg attributeUpdateMsg = TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data("{ \"enabled\": false }") .build(); @@ -164,14 +163,14 @@ public class DeviceStateTest { deviceState.process(ctx, TbMsg.newMsg() .type(TbMsgType.ALARM_CLEAR) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(JacksonUtil.toString(alarm)) .build()); TbMsg alarmDeleteNotification = TbMsg.newMsg() .type(TbMsgType.ALARM_DELETE) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(JacksonUtil.toString(alarm)) .build(); assertDoesNotThrow(() -> { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java index 01e42f042e..876b75ecc0 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java @@ -130,7 +130,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type("123456789") .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) .build(); @@ -154,11 +154,9 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -213,7 +211,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_STRING) .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())).thenReturn(theMsg); @@ -223,11 +221,9 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -237,7 +233,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg theMsg2 = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data("2") .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())).thenReturn(theMsg2); @@ -248,11 +244,9 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg2 = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg2); verify(ctx).tellSuccess(msg2); @@ -313,7 +307,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_STRING) .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())).thenReturn(theMsg); @@ -323,11 +317,9 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -337,7 +329,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg theMsg2 = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_STRING) .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())).thenReturn(theMsg2); @@ -361,11 +353,9 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg2 = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg2); verify(ctx).tellSuccess(msg2); @@ -443,7 +433,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_STRING) .build(); Mockito.when(ctx.newMsg(Mockito.any(), Mockito.any(TbMsgType.class), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) @@ -454,11 +444,9 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg); @@ -543,7 +531,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_STRING) .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) @@ -554,11 +542,9 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg); @@ -625,7 +611,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_STRING) .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) @@ -636,11 +622,9 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg); @@ -733,7 +717,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_STRING) .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) @@ -744,11 +728,9 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg); @@ -766,11 +748,9 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg2 = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg2); @@ -878,7 +858,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_STRING) .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) @@ -889,11 +869,9 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg); @@ -911,11 +889,9 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg2 = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg2); @@ -1008,7 +984,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_STRING) .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) @@ -1019,11 +995,9 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg); @@ -1035,11 +1009,9 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg2 = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg2); @@ -1145,7 +1117,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_STRING) .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) @@ -1156,11 +1128,9 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg); @@ -1172,11 +1142,9 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg2 = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg2); @@ -1261,7 +1229,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_STRING) .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) @@ -1272,11 +1240,9 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg); @@ -1294,11 +1260,9 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg2 = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg2); @@ -1379,7 +1343,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_STRING) .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) @@ -1390,11 +1354,9 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg); @@ -1474,7 +1436,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_STRING) .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) @@ -1485,11 +1447,10 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) - .ruleChainId(null) - .ruleNodeId(null) + .build(); // Mockito.reset(ctx); @@ -1583,7 +1544,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_STRING) .build(); @@ -1592,11 +1553,9 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg); @@ -1674,7 +1633,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_STRING) .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) @@ -1685,11 +1644,9 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg); @@ -1760,7 +1717,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_STRING) .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) @@ -1771,11 +1728,9 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg); @@ -1848,7 +1803,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { .thenReturn(device); Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.any(AttributeScope.class), Mockito.anySet())) .thenReturn(listListenableFutureWithLess); - Mockito.when(attributesService.find(eq(tenantId), eq(customerId), Mockito.any(AttributeScope.class), Mockito.anyString())) + Mockito.when(attributesService.find(eq(tenantId), eq(customerId), Mockito.any(AttributeScope.class), Mockito.anyString())) .thenReturn(emptyOptionalFuture); Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(AttributeScope.SERVER_SCOPE), Mockito.anyString())) .thenReturn(optionalListenableFutureWithLess); @@ -1856,7 +1811,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_STRING) .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) @@ -1867,11 +1822,9 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg); @@ -1954,7 +1907,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg theMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_STRING) .build(); when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) @@ -1965,11 +1918,9 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java index 914605a8d4..cd7de994b5 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java @@ -150,7 +150,7 @@ public class TbRabbitMqNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(data) .build(); node.onMsg(ctxMock, msg); @@ -186,7 +186,7 @@ public class TbRabbitMqNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); node.onMsg(ctxMock, msg); @@ -214,7 +214,7 @@ public class TbRabbitMqNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); node.onMsg(ctxMock, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbHttpClientTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbHttpClientTest.java index be4f3488ec..b42fa8662f 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbHttpClientTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbHttpClientTest.java @@ -143,13 +143,13 @@ public class TbHttpClientTest { var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(new DeviceId(EntityId.NULL_UUID)) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); var successMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(msg.getOriginator()) - .metaData(msg.getMetaData().copy()) + .copyMetaData(msg.getMetaData()) .data(msg.getData()) .build(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java index d94981cff3..9f5834cd46 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java @@ -145,7 +145,7 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) - .metaData(metaData.copy()) + .copyMetaData(metaData) .dataType(TbMsgDataType.JSON) .data(TbMsg.EMPTY_JSON_OBJECT) .ruleChainId(ruleChainId) @@ -214,7 +214,7 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) - .metaData(metaData.copy()) + .copyMetaData(metaData) .dataType(TbMsgDataType.JSON) .data(TbMsg.EMPTY_JSON_OBJECT) .ruleChainId(ruleChainId) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbSendRestApiCallReplyNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbSendRestApiCallReplyNodeTest.java index e7613a11cf..6bf34de3b1 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbSendRestApiCallReplyNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbSendRestApiCallReplyNodeTest.java @@ -92,7 +92,7 @@ public class TbSendRestApiCallReplyNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.REST_API_REQUEST) .originator(DEVICE_ID) - .metaData(new TbMsgMetaData(metadata).copy()) + .copyMetaData(new TbMsgMetaData(metadata)) .data(data) .build(); @@ -117,7 +117,7 @@ public class TbSendRestApiCallReplyNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.REST_API_REQUEST) .originator(DEVICE_ID) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(data) .build(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java index 61e2b45b42..11a29c67ba 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java @@ -95,11 +95,9 @@ public class TbSendRPCReplyNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(getDefaultMetadata().copy()) + .copyMetaData(getDefaultMetadata()) .dataType(TbMsgDataType.JSON) .data(DUMMY_DATA) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg); @@ -121,11 +119,9 @@ public class TbSendRPCReplyNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(defaultMetadata.copy()) + .copyMetaData(defaultMetadata) .dataType(TbMsgDataType.JSON) .data(DUMMY_DATA) - .ruleChainId(null) - .ruleNodeId(null) .build(); node.onMsg(ctx, msg); @@ -141,7 +137,7 @@ public class TbSendRPCReplyNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(entityId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); @@ -160,7 +156,7 @@ public class TbSendRPCReplyNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(TbMsg.EMPTY_STRING) .build(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java index a22554fd56..4aa38b230a 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java @@ -110,7 +110,7 @@ public class TbSendRPCRequestNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) - .metaData(msgMetadata.copy()) + .copyMetaData(msgMetadata) .data(MSG_DATA) .build(); node.onMsg(ctxMock, msg); @@ -136,7 +136,7 @@ public class TbSendRPCRequestNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(MSG_DATA) .build(); node.onMsg(ctxMock, msg); @@ -162,7 +162,7 @@ public class TbSendRPCRequestNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.TO_SERVER_RPC_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(MSG_DATA) .build(); node.onMsg(ctxMock, msg); @@ -188,7 +188,7 @@ public class TbSendRPCRequestNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.TO_SERVER_RPC_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(data) .build(); node.onMsg(ctxMock, msg); @@ -208,7 +208,7 @@ public class TbSendRPCRequestNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) - .metaData(metadata.copy()) + .copyMetaData(metadata) .data(MSG_DATA) .build(); node.onMsg(ctxMock, msg); @@ -228,7 +228,7 @@ public class TbSendRPCRequestNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) - .metaData(metadata.copy()) + .copyMetaData(metadata) .data(MSG_DATA) .build(); node.onMsg(ctxMock, msg); @@ -248,7 +248,7 @@ public class TbSendRPCRequestNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) - .metaData(metadata.copy()) + .copyMetaData(metadata) .data(MSG_DATA) .build(); node.onMsg(ctxMock, msg); @@ -268,7 +268,7 @@ public class TbSendRPCRequestNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) - .metaData(metadata.copy()) + .copyMetaData(metadata) .data(MSG_DATA) .build(); node.onMsg(ctxMock, msg); @@ -288,7 +288,7 @@ public class TbSendRPCRequestNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) - .metaData(metadata.copy()) + .copyMetaData(metadata) .data(MSG_DATA) .build(); node.onMsg(ctxMock, msg); @@ -308,7 +308,7 @@ public class TbSendRPCRequestNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) - .metaData(metadata.copy()) + .copyMetaData(metadata) .data(MSG_DATA) .build(); node.onMsg(ctxMock, msg); @@ -328,7 +328,7 @@ public class TbSendRPCRequestNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) - .metaData(metadata.copy()) + .copyMetaData(metadata) .data(MSG_DATA) .build(); node.onMsg(ctxMock, msg); @@ -348,7 +348,7 @@ public class TbSendRPCRequestNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) - .metaData(metadata.copy()) + .copyMetaData(metadata) .data(MSG_DATA) .build(); node.onMsg(ctxMock, msg); @@ -366,7 +366,7 @@ public class TbSendRPCRequestNodeTest { TbMsg msg = TbMsg.newMsg() .type(msgType) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(MSG_DATA) .build(); node.onMsg(ctxMock, msg); @@ -390,7 +390,7 @@ public class TbSendRPCRequestNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) - .metaData(metadata.copy()) + .copyMetaData(metadata) .data(MSG_DATA) .build(); node.onMsg(ctxMock, msg); @@ -419,7 +419,7 @@ public class TbSendRPCRequestNodeTest { TbMsg outMsg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); @@ -439,7 +439,7 @@ public class TbSendRPCRequestNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(MSG_DATA) .build(); node.onMsg(ctxMock, msg); @@ -453,7 +453,7 @@ public class TbSendRPCRequestNodeTest { TbMsg outMsg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); @@ -472,7 +472,7 @@ public class TbSendRPCRequestNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(MSG_DATA) .build(); node.onMsg(ctxMock, msg); @@ -489,7 +489,7 @@ public class TbSendRPCRequestNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(entityId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); node.onMsg(ctxMock, msg); @@ -507,7 +507,7 @@ public class TbSendRPCRequestNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data("{\"" + key + "\": \"value\"}") .build(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java index 470a397ea4..3822ee7d5f 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java @@ -167,7 +167,7 @@ class TbMsgAttributesNodeTest extends AbstractRuleNodeUpgradeTest { var testTbMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(md.copy()) + .copyMetaData(md) .data(TbMsg.EMPTY_STRING) .build(); List testAttrList = List.of(new BaseAttributeKvEntry(0L, new StringDataEntry("testKey", "testValue"))); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNodeTest.java index 80259b34c0..0714e13473 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNodeTest.java @@ -142,7 +142,7 @@ public class TbMsgDeleteAttributesNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(deviceId) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(data) .callback(callback) .build(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java index 3bd7a67794..af895d6f52 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java @@ -99,7 +99,7 @@ public class TbMsgTimeseriesNodeTest { TbMsg msg = TbMsg.newMsg() .type(msgType) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_ARRAY) .build(); node.onMsg(ctxMock, msg); @@ -130,7 +130,7 @@ public class TbMsgTimeseriesNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(data) .build(); @@ -172,7 +172,7 @@ public class TbMsgTimeseriesNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(new TbMsgMetaData(metadata).copy()) + .copyMetaData(new TbMsgMetaData(metadata)) .data(data) .build(); @@ -215,7 +215,7 @@ public class TbMsgTimeseriesNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(metadata.copy()) + .copyMetaData(metadata) .data(data) .build(); node.onMsg(ctxMock, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java index 851179bc04..2a0e0b73f7 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java @@ -179,7 +179,7 @@ public class TbChangeOriginatorNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); TbMsg expectedMsg = msg.transform() @@ -209,7 +209,7 @@ public class TbChangeOriginatorNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(ASSET_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); TbMsg expectedMsg = msg.transform() @@ -236,7 +236,7 @@ public class TbChangeOriginatorNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(ASSET_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); @@ -275,7 +275,7 @@ public class TbChangeOriginatorNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(alarmId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); TbMsg expectedMsg = msg.transform() @@ -308,7 +308,7 @@ public class TbChangeOriginatorNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(data) .build(); TbMsg expectedMsg = msg.transform() @@ -351,7 +351,7 @@ public class TbChangeOriginatorNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeTest.java index 427181c5af..3e214d534d 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeTest.java @@ -199,7 +199,7 @@ public class TbCopyKeysNodeTest { return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) - .metaData(new TbMsgMetaData(mdMap).copy()) + .copyMetaData(new TbMsgMetaData(mdMap)) .data(data) .callback(callback) .build(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNodeTest.java index 93b34182a8..036dc5440b 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNodeTest.java @@ -176,7 +176,7 @@ public class TbDeleteKeysNodeTest { return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) - .metaData(new TbMsgMetaData(mdMap).copy()) + .copyMetaData(new TbMsgMetaData(mdMap)) .data(data) .callback(callback) .build(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbJsonPathNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbJsonPathNodeTest.java index 298df82d2a..ab8c9a44ba 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbJsonPathNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbJsonPathNodeTest.java @@ -174,7 +174,7 @@ public class TbJsonPathNodeTest { return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) - .metaData(new TbMsgMetaData(mdMap).copy()) + .copyMetaData(new TbMsgMetaData(mdMap)) .data(data) .callback(callback) .build(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbMsgDeduplicationNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbMsgDeduplicationNodeTest.java index 84b648823f..91dc20bd9a 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbMsgDeduplicationNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbMsgDeduplicationNodeTest.java @@ -106,7 +106,7 @@ public class TbMsgDeduplicationNodeTest extends AbstractRuleNodeUpgradeTest { return TbMsg.newMsg() .type(type) .originator(originator) - .metaData(metaData.copy().copy()) + .copyMetaData(metaData.copy()) .data(data) .build(); }).when(ctx).newMsg(isNull(), eq(TbMsgType.DEDUPLICATION_TIMEOUT_SELF_MSG), nullable(EntityId.class), any(TbMsgMetaData.class), any(String.class)); @@ -461,7 +461,7 @@ public class TbMsgDeduplicationNodeTest extends AbstractRuleNodeUpgradeTest { .queueName(DataConstants.MAIN_QUEUE_NAME) .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(deviceId) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(JacksonUtil.toString(dataNode)) .build(); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeTest.java index 61851945c0..0c7f291db1 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeTest.java @@ -191,7 +191,7 @@ public class TbRenameKeysNodeTest { return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) - .metaData(new TbMsgMetaData(mdMap).copy()) + .copyMetaData(new TbMsgMetaData(mdMap)) .data(data) .callback(callback) .build(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNodeTest.java index b811934718..895ae96db8 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNodeTest.java @@ -135,7 +135,7 @@ public class TbSplitArrayMsgNodeTest { return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) - .metaData(new TbMsgMetaData(mdMap).copy()) + .copyMetaData(new TbMsgMetaData(mdMap)) .data(data) .callback(callback) .build(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java index 094cc4ac18..72f893aa86 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java @@ -64,7 +64,7 @@ public class TbTransformMsgNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(null) - .metaData(metaData.copy()) + .copyMetaData(metaData) .dataType(TbMsgDataType.JSON) .data(rawJson) .ruleChainId(ruleChainId) @@ -73,7 +73,7 @@ public class TbTransformMsgNodeTest { TbMsg transformedMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(null) - .metaData(metaData.copy()) + .copyMetaData(metaData) .dataType(TbMsgDataType.JSON) .data("{new}") .ruleChainId(ruleChainId) @@ -100,7 +100,7 @@ public class TbTransformMsgNodeTest { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(null) - .metaData(metaData.copy()) + .copyMetaData(metaData) .dataType(TbMsgDataType.JSON) .data(rawJson) .ruleChainId(ruleChainId) From c60da6829b06bea3dac73e08ff9c5278c89e3335 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 19 Dec 2024 16:19:26 +0200 Subject: [PATCH 019/108] Fix msg metadata copied twice --- .../rule/engine/transform/TbMsgDeduplicationNodeTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbMsgDeduplicationNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbMsgDeduplicationNodeTest.java index 91dc20bd9a..cbff064ec3 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbMsgDeduplicationNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbMsgDeduplicationNodeTest.java @@ -106,7 +106,7 @@ public class TbMsgDeduplicationNodeTest extends AbstractRuleNodeUpgradeTest { return TbMsg.newMsg() .type(type) .originator(originator) - .copyMetaData(metaData.copy()) + .copyMetaData(metaData) .data(data) .build(); }).when(ctx).newMsg(isNull(), eq(TbMsgType.DEDUPLICATION_TIMEOUT_SELF_MSG), nullable(EntityId.class), any(TbMsgMetaData.class), any(String.class)); From 9fe5f7199e7a3a47cc966307f5683102ec08f1f3 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 19 Dec 2024 16:48:30 +0200 Subject: [PATCH 020/108] UI: Dynamic form implementation. Migration from JSON schema form. --- .../add-widget-dialog.component.ts | 3 +- .../dashboard-page/edit-widget.component.ts | 3 +- .../filter-predicate-value.component.html | 3 +- .../filter/key-filter-dialog.component.html | 3 +- .../filter/user-filter-dialog.component.html | 3 +- .../widget/lib/scada/scada-symbol.models.ts | 99 ++-- .../dynamic-form-array.component.html | 74 +++ .../dynamic-form-array.component.scss | 72 +++ .../dynamic-form-array.component.ts | 161 +++++++ .../dynamic-form-properties.component.html | 1 + .../dynamic-form-properties.component.ts | 15 +- ...dynamic-form-property-panel.component.html | 422 +++++++++++++----- ...dynamic-form-property-panel.component.scss | 21 + .../dynamic-form-property-panel.component.ts | 131 +++++- .../dynamic-form-property-row.component.ts | 41 +- .../dynamic-form-select-item-row.component.ts | 5 +- .../dynamic-form-select-items.component.html | 1 + .../dynamic-form-select-items.component.ts | 10 +- .../dynamic-form/dynamic-form.component.html | 203 +++++++-- .../dynamic-form/dynamic-form.component.scss | 21 + .../dynamic-form/dynamic-form.component.ts | 70 ++- .../common/widget-settings-common.module.ts | 9 +- .../widget/widget-component.service.ts | 7 + .../home/models/widget-component.models.ts | 4 + .../entity-view/entity-view.component.html | 18 +- .../scada-symbol-behaviors.component.html | 1 + .../scada-symbol-behaviors.component.ts | 4 +- .../shared/components/js-func.component.ts | 10 +- .../string-items-list.component.html | 3 +- .../components/string-items-list.component.ts | 3 + .../components/time/datetime.component.html | 46 +- .../components/time/datetime.component.scss | 60 +++ .../components/time/datetime.component.ts | 32 +- .../app/shared/models/dynamic-form.models.ts | 421 +++++++++++++++-- ui-ngx/src/app/shared/models/widget.models.ts | 1 + .../assets/locale/locale.constant-en_US.json | 35 +- ui-ngx/src/form.scss | 10 + 37 files changed, 1669 insertions(+), 357 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-array.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-array.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-array.component.ts create mode 100644 ui-ngx/src/app/shared/components/time/datetime.component.scss diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.ts index 4617f7a4ed..860825f5e8 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.ts @@ -109,7 +109,8 @@ export class AddWidgetDialogComponent extends DialogComponent diff --git a/ui-ngx/src/app/modules/home/components/filter/key-filter-dialog.component.html b/ui-ngx/src/app/modules/home/components/filter/key-filter-dialog.component.html index 2b4ed91214..bb299bc4bc 100644 --- a/ui-ngx/src/app/modules/home/components/filter/key-filter-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/key-filter-dialog.component.html @@ -101,8 +101,7 @@ diff --git a/ui-ngx/src/app/modules/home/components/filter/user-filter-dialog.component.html b/ui-ngx/src/app/modules/home/components/filter/user-filter-dialog.component.html index c5b19aac1f..bc05db8c46 100644 --- a/ui-ngx/src/app/modules/home/components/filter/user-filter-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/user-filter-dialog.component.html @@ -47,8 +47,7 @@ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts index d646990351..16ac07e6fd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts @@ -65,7 +65,12 @@ import { catchError, map, take, takeUntil } from 'rxjs/operators'; import { isSvgIcon, splitIconName } from '@shared/models/icon.models'; import { MatIconRegistry } from '@angular/material/icon'; import { RafService } from '@core/services/raf.service'; -import { defaultFormPropertyValue, FormProperty, FormPropertyType } from '@shared/models/dynamic-form.models'; +import { + defaultFormPropertyValue, + defaultPropertyValue, + FormProperty, + FormPropertyType +} from '@shared/models/dynamic-form.models'; export interface ScadaSymbolApi { generateElementId: () => string; @@ -630,7 +635,9 @@ export class ScadaSymbolObject { elements.push(element); } for (const property of this.metadata.properties) { - this.context.properties[property.id] = this.getPropertyValue(property.id); + if (property.type !== FormPropertyType.htmlSection) { + this.context.properties[property.id] = this.getPropertyValue(this.metadata.properties, this.settings.properties, property.id); + } } for (const tag of this.metadata.tags) { if (tag.actions) { @@ -967,9 +974,8 @@ export class ScadaSymbolObject { return Array.isArray(element) ? element : [element]; } - private getProperty(...ids: string[]): FormProperty { + private getProperty(properties: FormProperty[], ...ids: string[]): FormProperty { let found: FormProperty; - let properties = this.metadata.properties; for (const id of ids) { if (properties) { found = properties.find(p => p.id === id); @@ -985,9 +991,9 @@ export class ScadaSymbolObject { return found; } - private getSettingsValue(...ids: string[]): any { + private getSettingsValue(settings: {[id: string]: any}, ...ids: string[]): any { let found: any; - let properties = this.settings.properties; + let properties = settings; for (const id of ids) { if (properties) { found = properties[id]; @@ -1003,43 +1009,72 @@ export class ScadaSymbolObject { return found; } - private getPropertyValue(...ids: string[]): any { - const property = this.getProperty(...ids); + private getPropertyValue(properties: FormProperty[], settings: {[id: string]: any}, ...ids: string[]): any { + const property = this.getProperty(properties, ...ids); if (property) { - if (property.type === FormPropertyType.fieldset) { + if (property.type === FormPropertyType.array) { + const arrayValue = []; + if (property.arrayItemType !== FormPropertyType.htmlSection) { + const settingsValue = this.getSettingsValue(settings, ...ids); + if (settingsValue && Array.isArray(settingsValue)) { + for (const settingsElement of settingsValue) { + let value: any; + if (property.arrayItemType === FormPropertyType.fieldset) { + const propertyValue: {[id: string]: any} = {}; + for (const childProperty of property.properties) { + if (childProperty.type !== FormPropertyType.htmlSection) { + propertyValue[childProperty.id] = this.getPropertyValue(property.properties, settingsElement, childProperty.id); + } + } + value = propertyValue; + } else { + value = this.convertPropertyValue(property, settingsElement); + } + arrayValue.push(value); + } + } + } + return arrayValue; + } else if (property.type === FormPropertyType.fieldset) { const propertyValue: {[id: string]: any} = {}; for (const childProperty of property.properties) { - propertyValue[childProperty.id] = this.getPropertyValue(...ids, childProperty.id); + if (childProperty.type !== FormPropertyType.htmlSection) { + propertyValue[childProperty.id] = this.getPropertyValue(properties, settings, ...ids, childProperty.id); + } } return propertyValue; } else { - const value = this.getSettingsValue(...ids); - if (isDefinedAndNotNull(value)) { - if (property.type === FormPropertyType.color_settings) { - return ColorProcessor.fromSettings(value); - } else if (property.type === FormPropertyType.text) { - const result = this.ctx.utilsService.customTranslation(value, value); - const entityInfo = this.ctx.defaultSubscription.getFirstEntityInfo(); - return createLabelFromSubscriptionEntityInfo(entityInfo, result); - } - return value; - } else { - switch (property.type) { - case FormPropertyType.text: - return ''; - case FormPropertyType.number: - return 0; - case FormPropertyType.color: - return '#000'; - case FormPropertyType.color_settings: - return ColorProcessor.fromSettings(constantColor('#000')); - } - } + const value = this.getSettingsValue(settings, ...ids); + return this.convertPropertyValue(property, value); } } else { return ''; } } + + private convertPropertyValue(property: FormProperty, value: any): any { + if (isDefinedAndNotNull(value)) { + if (property.type === FormPropertyType.color_settings) { + return ColorProcessor.fromSettings(value); + } else if ([FormPropertyType.text, FormPropertyType.textarea].includes(property.type)) { + const result = this.ctx.utilsService.customTranslation(value, value); + const entityInfo = this.ctx.defaultSubscription.getFirstEntityInfo(); + return createLabelFromSubscriptionEntityInfo(entityInfo, result); + } + return value; + } else { + switch (property.type) { + case FormPropertyType.color_settings: + return ColorProcessor.fromSettings(constantColor('#000')); + case FormPropertyType.font: + case FormPropertyType.units: + case FormPropertyType.icon: + return null; + default: + return defaultPropertyValue(property.type); + } + } + } } const scadaSymbolAnimationId = 'scadaSymbolAnimation'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-array.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-array.component.html new file mode 100644 index 0000000000..b15964a5af --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-array.component.html @@ -0,0 +1,74 @@ + +
+ + + +
{{ title }}
+
+
+ +
+
+
+
{{$index + 1}}
+
+ + +
+ + +
+
+
+
+ +
+
+
+
+ + + {{ 'dynamic-form.property.no-items' | translate }} + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-array.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-array.component.scss new file mode 100644 index 0000000000..1935e29a26 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-array.component.scss @@ -0,0 +1,72 @@ +/** + * 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 "../../../../../../../../../scss/constants"; + +.tb-dynamic-form-array { + .tb-dynamic-form-array-body { + display: flex; + flex-direction: column; + gap: 12px; + } +} +.tb-dynamic-form-array-row { + background: #fff; + display: flex; + flex-direction: row; + place-content: center flex-start; + align-items: flex-start; + .tb-dynamic-form-item-index-container { + width: 36px; + height: 56px; + display: flex; + flex-direction: row; + place-content: flex-start; + align-items: center; + .tb-dynamic-form-item-index { + width: 24px; + height: 24px; + position: relative; + font-weight: 400; + font-size: 16px; + display: flex; + align-items: center; + place-content: center; + color: $tb-primary-color; + &:before { + content: ""; + display: block; + width: 100%; + height: 100%; + position: absolute; + left: 0; + top: 0; + background-color: $tb-primary-color; + opacity: 0.06; + border-radius: 100%; + } + } + } + .tb-dynamic-form-array-buttons { + display: flex; + flex-direction: row; + button.mat-mdc-icon-button.mat-mdc-button-base { + color: rgba(0, 0, 0, 0.38); + &.tb-hidden { + visibility: hidden; + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-array.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-array.component.ts new file mode 100644 index 0000000000..081adbfcea --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-array.component.ts @@ -0,0 +1,161 @@ +/// +/// 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, DestroyRef, forwardRef, Input, OnInit, ViewEncapsulation } from '@angular/core'; +import { + AbstractControl, + ControlValueAccessor, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + UntypedFormArray, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, + Validator +} from '@angular/forms'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { defaultFormPropertyValue, FormProperty } from '@shared/models/dynamic-form.models'; +import { CdkDragDrop } from '@angular/cdk/drag-drop'; + +@Component({ + selector: 'tb-dynamic-form-array', + templateUrl: './dynamic-form-array.component.html', + styleUrls: ['./dynamic-form-array.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => DynamicFormArrayComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => DynamicFormArrayComponent), + multi: true + } + ], + encapsulation: ViewEncapsulation.None +}) +export class DynamicFormArrayComponent implements ControlValueAccessor, OnInit, Validator { + + @Input() + disabled: boolean; + + @Input() + itemProperty: FormProperty; + + @Input() + title: string; + + propertiesFormGroup: UntypedFormGroup; + + get dragEnabled(): boolean { + return !this.disabled && this.propertiesFormArray().controls.length > 1; + } + + private propagateChange = (_val: any) => {}; + + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { + } + + ngOnInit() { + this.propertiesFormGroup = this.fb.group({ + properties: this.fb.array([]) + }); + this.propertiesFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( + () => { + const properties: {[id: string]: any}[] = this.propertiesFormGroup.get('properties').value; + const value = properties.map(prop => prop[this.itemProperty.id]); + this.propagateChange(value); + } + ); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.propertiesFormGroup.disable({emitEvent: false}); + } else { + this.propertiesFormGroup.enable({emitEvent: false}); + } + } + + writeValue(values: any[] | undefined): void { + this.propertiesFormGroup.setControl('properties', this.preparePropertiesFormArray(values || []), {emitEvent: false}); + } + + public validate(_c: UntypedFormControl) { + const valid = this.propertiesFormGroup.valid; + return valid ? null : { + properties: { + valid: false, + }, + }; + } + + propertyDrop(event: CdkDragDrop) { + const propertiesArray = this.propertiesFormGroup.get('properties') as UntypedFormArray; + const property = propertiesArray.at(event.previousIndex); + propertiesArray.removeAt(event.previousIndex, {emitEvent: false}); + propertiesArray.insert(event.currentIndex, property, {emitEvent: true}); + } + + propertiesFormArray(): UntypedFormArray { + return this.propertiesFormGroup.get('properties') as UntypedFormArray; + } + + trackByProperty(_index: number, propertyControl: AbstractControl): any { + return propertyControl; + } + + removeProperty(index: number, emitEvent = true) { + (this.propertiesFormGroup.get('properties') as UntypedFormArray).removeAt(index, {emitEvent}); + } + + addProperty() { + const property = { + [this.itemProperty.id]: defaultFormPropertyValue(this.itemProperty) + }; + const propertiesArray = this.propertiesFormGroup.get('properties') as UntypedFormArray; + const propertyControl = this.fb.control(property, []); + propertiesArray.push(propertyControl); + setTimeout(() => { + propertyControl.updateValueAndValidity(); + }); + } + + private preparePropertiesFormArray(values: any[] | undefined): UntypedFormArray { + const propertiesControls: Array = []; + if (values) { + values.forEach((value) => { + const property = { + [this.itemProperty.id]: value + }; + propertiesControls.push(this.fb.control(property, [])); + }); + } + return this.fb.array(propertiesControls); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.html index 72d008b84e..cfdcfacfc4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.html @@ -28,6 +28,7 @@ [cdkDropListDisabled]="!dragEnabled" (cdkDropListDropped)="propertyDrop($event)">
p.type === FormPropertyType.switch).map(p => p.id); } - public validate(c: UntypedFormControl) { + public validate(_c: UntypedFormControl) { this.errorText = ''; const propertiesArray = this.propertiesFormGroup.get('properties') as UntypedFormArray; const notUniqueControls = @@ -189,15 +188,15 @@ export class DynamicFormPropertiesComponent implements ControlValueAccessor, OnI propertyDrop(event: CdkDragDrop) { const propertiesArray = this.propertiesFormGroup.get('properties') as UntypedFormArray; const property = propertiesArray.at(event.previousIndex); - propertiesArray.removeAt(event.previousIndex); - propertiesArray.insert(event.currentIndex, property); + propertiesArray.removeAt(event.previousIndex, {emitEvent: false}); + propertiesArray.insert(event.currentIndex, property, {emitEvent: true}); } propertiesFormArray(): UntypedFormArray { return this.propertiesFormGroup.get('properties') as UntypedFormArray; } - trackByProperty(index: number, propertyControl: AbstractControl): any { + trackByProperty(_index: number, propertyControl: AbstractControl): any { return propertyControl; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.html index 5b28248bf2..c10aaaf803 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.html @@ -46,7 +46,139 @@
-
+
+
dynamic-form.property.array-item
+
+
dynamic-form.property.item-type
+ + + + {{ formPropertyTypeTranslations.get(type) | translate }} + + + +
+
+
dynamic-form.property.item-name
+ + + +
+ + +
+ + + + +
+ + {{ 'dynamic-form.property.value-required' | translate }} + +
+
+ + + + {{ 'dynamic-form.property.advanced-ui-settings' | translate }} + + + + +
+
dynamic-form.property.sub-label
+ + + +
+
+ + {{ 'dynamic-form.property.vertical-divider-after' | translate }} + +
+
+
+
dynamic-form.property.input-field-suffix
+ + + +
+
+
dynamic-form.property.disable-on-property
+ + + + + {{ prop }} + + + +
+
+ + +
+ + + + +
+
+
+
+
+ + +
+
+ + + +
+
dynamic-form.property.textarea-rows
+ + + +
+
dynamic-form.property.number-settings
dynamic-form.property.min
@@ -68,7 +200,7 @@
-
+
@@ -81,20 +213,60 @@
- +
+ + + + {{ 'dynamic-form.property.html-section-settings' | translate }} + + + +
+ + +
+ + +
+
+
+
- {{ 'dynamic-form.property.select-options' | translate }} + {{ (propertyFormGroup.get('type').value === FormPropertyType.select ? 'dynamic-form.property.select-options' : 'dynamic-form.property.radio-button-options') | translate }} -
+
+
dynamic-form.property.buttons-direction
+ + {{ 'dynamic-form.property.direction-column' | translate }} + {{ 'dynamic-form.property.direction-row' | translate }} + +
+
{{ 'dynamic-form.property.enable-multiple-select' | translate }}
+
+ + {{ 'dynamic-form.property.allow-empty-select-option' | translate }} + +
@@ -102,23 +274,113 @@
-
+
+
dynamic-form.property.help-id
+ + + +
+ +
+
dynamic-form.property.datetime-type
+ + {{ 'dynamic-form.property.datetime-type-date' | translate }} + {{ 'dynamic-form.property.datetime-type-time' | translate }} + {{ 'dynamic-form.property.datetime-type-datetime' | translate }} + +
+
+ + {{ 'dynamic-form.property.enable-clear-button' | translate }} + +
+
+
+ + dynamic-form.property.default-value + + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
dynamic-form.property.default-value
+ + +
{{ item.label | customTranslate }}
+
+
+
+
dynamic-form.property.default-value
- - + - - - @@ -128,23 +390,24 @@ [step]="propertyFormGroup.get('step').value" type="number" placeholder="{{ 'widget-config.set' | translate }}"> - - - - - + {{ item.label | customTranslate }} @@ -155,97 +418,38 @@ + + + + +
-
- - {{ 'dynamic-form.property.value-required' | translate }} - -
-
- - - - {{ 'dynamic-form.property.advanced-ui-settings' | translate }} - - - -
-
dynamic-form.property.sub-label
- - - -
-
- - {{ 'dynamic-form.property.vertical-divider-after' | translate }} - -
-
-
dynamic-form.property.input-field-suffix
- - - -
-
-
dynamic-form.property.disable-on-property
- - - - - {{ prop }} - - - -
-
- - -
-
-
dynamic-form.property.property-row-classes
- - - - {{ clazz }} - - - -
-
-
dynamic-form.property.property-field-classes
- - - - {{ clazz }} - - - -
-
-
-
-
-
- - -
-
+
+ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.scss index 1d093f6a22..da613d5e61 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.scss @@ -46,4 +46,25 @@ justify-content: flex-end; align-items: flex-end; } + + .tb-form-row.tb-radios-property { + &.direction-column { + flex-direction: column; + align-items: flex-start; + gap: 4px; + } + .mat-mdc-radio-group { + overflow: hidden; + .mat-mdc-radio-button { + overflow: hidden; + .mdc-form-field { + overflow: hidden; + width: 100%; + .mdc-label { + overflow: hidden; + } + } + } + } + } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.ts index a9cb9255a3..ee19d25a3d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.ts @@ -17,20 +17,21 @@ import { Component, DestroyRef, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { TbPopoverComponent } from '@shared/components/popover.component'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; -import { ValueType } from '@shared/models/constants'; +import { ContentType, ValueType } from '@shared/models/constants'; import { + defaultPropertyValue, FormProperty, formPropertyFieldClasses, formPropertyRowClasses, FormPropertyType, formPropertyTypes, formPropertyTypeTranslations, - FormSelectItem + FormSelectItem, + isPropertyTypeAllowedForRow } from '@shared/models/dynamic-form.models'; -import { - defaultPropertyValue -} from '@home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-row.component'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { isUndefinedOrNull } from '@core/utils'; +import { StringItemsOption } from '@shared/components/string-items-list.component'; @Component({ selector: 'tb-dynamic-form-property-panel', @@ -44,12 +45,29 @@ export class DynamicFormPropertyPanelComponent implements OnInit { FormPropertyType = FormPropertyType; + ContentType = ContentType; + formPropertyTypes = formPropertyTypes; + arrayItemFormPropertyTypes = formPropertyTypes.filter(t => t !== FormPropertyType.array); formPropertyTypeTranslations = formPropertyTypeTranslations; - formPropertyRowClasses = formPropertyRowClasses; + formPropertyRowClasses: StringItemsOption[] = formPropertyRowClasses.map(clazz => ({name: clazz, value: clazz})); + + formPropertyFieldClasses: StringItemsOption[] = formPropertyFieldClasses.map(clazz => ({name: clazz, value: clazz})); - formPropertyFieldClasses = formPropertyFieldClasses; + isPropertyTypeAllowedForRow = isPropertyTypeAllowedForRow; + + get propertyItemType(): FormPropertyType | any { + if (this.isArray) { + return this.propertyFormGroup.get('arrayItemType').value; + } else { + return this.propertyFormGroup.get('type').value; + } + } + + get isArray(): boolean { + return this.propertyFormGroup.get('type').value === FormPropertyType.array; + } @Input() isAdd = false; @@ -81,13 +99,15 @@ export class DynamicFormPropertyPanelComponent implements OnInit { ngOnInit(): void { this.panelTitle = this.isAdd ? 'dynamic-form.property.add-property' : 'dynamic-form.property.property-settings'; - this.propertyType = this.property.type; + this.propertyType = this.property.type === FormPropertyType.array ? this.property.arrayItemType : this.property.type; this.propertyFormGroup = this.fb.group( { id: [this.property.id, [Validators.required]], name: [this.property.name, [Validators.required]], group: [this.property.group, []], type: [this.property.type, [Validators.required]], + arrayItemType: [this.property.arrayItemType, [Validators.required]], + arrayItemName: [this.property.arrayItemName, []], default: [this.property.default, []], required: [this.property.required, []], subLabel: [this.property.subLabel, []], @@ -95,14 +115,22 @@ export class DynamicFormPropertyPanelComponent implements OnInit { fieldSuffix: [this.property.fieldSuffix, []], disableOnProperty: [this.property.disableOnProperty, []], condition: [this.property.condition, []], - rowClass: [(this.property.rowClass || '').split(' '), []], - fieldClass: [(this.property.fieldClass || '').split(' '), []], + rowClass: [this.property.rowClass ? this.property.rowClass.split(' ') : [], []], + fieldClass: [this.property.fieldClass ? this.property.fieldClass.split(' ') : [], []], + rows: [this.property.rows, [Validators.min(1)]], min: [this.property.min, []], max: [this.property.max, []], step: [this.property.step, [Validators.min(0)]], properties: [this.property.properties, []], multiple: [this.property.multiple, []], - items: [this.property.items, []] + allowEmptyOption: [this.property.allowEmptyOption, []], + items: [this.property.items, []], + helpId: [this.property.helpId, []], + direction: [this.property.direction || 'column', []], + allowClear: [this.property.allowClear || true, []], + dateTimeType: [this.property.dateTimeType || 'datetime', []], + htmlContent: [this.property.htmlContent || '', []], + htmlClassList: [this.property.htmlClassList || [], []] } ); if (this.disabled) { @@ -113,7 +141,11 @@ export class DynamicFormPropertyPanelComponent implements OnInit { ).subscribe(() => { this.updateValidators(); }); - + this.propertyFormGroup.get('arrayItemType').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.updateValidators(); + }); this.propertyFormGroup.get('items').valueChanges.pipe( takeUntilDestroyed(this.destroyRef) ).subscribe(() => { @@ -140,7 +172,19 @@ export class DynamicFormPropertyPanelComponent implements OnInit { } private updateValidators() { - const type: FormPropertyType = this.propertyFormGroup.get('type').value; + if (this.isArray) { + this.propertyFormGroup.get('arrayItemType').enable({emitEvent: false}); + this.propertyFormGroup.get('arrayItemName').enable({emitEvent: false}); + } else { + this.propertyFormGroup.get('arrayItemType').disable({emitEvent: false}); + this.propertyFormGroup.get('arrayItemName').disable({emitEvent: false}); + } + const type = this.propertyItemType; + if (type === FormPropertyType.textarea) { + this.propertyFormGroup.get('rows').enable({emitEvent: false}); + } else { + this.propertyFormGroup.get('rows').disable({emitEvent: false}); + } if (type === FormPropertyType.number) { this.propertyFormGroup.get('min').enable({emitEvent: false}); this.propertyFormGroup.get('max').enable({emitEvent: false}); @@ -157,27 +201,73 @@ export class DynamicFormPropertyPanelComponent implements OnInit { this.propertyFormGroup.get('default').enable({emitEvent: false}); this.propertyFormGroup.get('properties').disable({emitEvent: false}); } - if (type === FormPropertyType.select) { - this.propertyFormGroup.get('multiple').enable({emitEvent: false}); + if ([FormPropertyType.select, FormPropertyType.radios].includes(type)) { this.propertyFormGroup.get('items').enable({emitEvent: false}); + if (type === FormPropertyType.select) { + this.propertyFormGroup.get('multiple').enable({emitEvent: false}); + const multiple: boolean = this.propertyFormGroup.get('multiple').value; + if (multiple) { + this.propertyFormGroup.get('allowEmptyOption').disable({emitEvent: false}); + } else { + this.propertyFormGroup.get('allowEmptyOption').enable({emitEvent: false}); + } + } } else { this.propertyFormGroup.get('multiple').disable({emitEvent: false}); + this.propertyFormGroup.get('allowEmptyOption').disable({emitEvent: false}); this.propertyFormGroup.get('items').disable({emitEvent: false}); } + if (type === FormPropertyType.datetime) { + this.propertyFormGroup.get('allowClear').enable({emitEvent: false}); + this.propertyFormGroup.get('dateTimeType').enable({emitEvent: false}); + } else { + this.propertyFormGroup.get('allowClear').disable({emitEvent: false}); + this.propertyFormGroup.get('dateTimeType').disable({emitEvent: false}); + } + if (type === FormPropertyType.htmlSection) { + this.propertyFormGroup.get('htmlContent').enable({emitEvent: false}); + this.propertyFormGroup.get('htmlClassList').enable({emitEvent: false}); + } else { + this.propertyFormGroup.get('htmlContent').disable({emitEvent: false}); + this.propertyFormGroup.get('htmlClassList').disable({emitEvent: false}); + } + if ([FormPropertyType.javascript, FormPropertyType.markdown].includes(type)) { + this.propertyFormGroup.get('helpId').enable({emitEvent: false}); + } else { + this.propertyFormGroup.get('helpId').disable({emitEvent: false}); + } if (this.propertyType !== type) { const defaultValue = defaultPropertyValue(type); this.propertyFormGroup.get('default').patchValue(defaultValue, {emitEvent: false}); this.propertyType = type; + if (type === FormPropertyType.textarea) { + if (isUndefinedOrNull(this.propertyFormGroup.get('rows').value)) { + this.propertyFormGroup.get('rows').patchValue(2, {emitEvent: false}); + } + } + if (type === FormPropertyType.radios) { + if (isUndefinedOrNull(this.propertyFormGroup.get('direction').value)) { + this.propertyFormGroup.get('direction').patchValue('column', {emitEvent: false}); + } + } + if (type === FormPropertyType.datetime) { + if (isUndefinedOrNull(this.propertyFormGroup.get('dateTimeType').value)) { + this.propertyFormGroup.get('dateTimeType').patchValue('datetime', {emitEvent: false}); + } + if (isUndefinedOrNull(this.propertyFormGroup.get('allowClear').value)) { + this.propertyFormGroup.get('allowClear').patchValue(true, {emitEvent: false}); + } + } } } private onSelectItemsChange() { - const type: FormPropertyType = this.propertyFormGroup.get('type').value; + const type = this.propertyItemType; const multiple: boolean = this.propertyFormGroup.get('multiple').value; const defaultValue: any = this.propertyFormGroup.get('default').value; const items: FormSelectItem[] = this.propertyFormGroup.get('items').value; - if (defaultValue && type === FormPropertyType.select) { - if (multiple) { + if (defaultValue && [FormPropertyType.select, FormPropertyType.radios].includes(type)) { + if (multiple && FormPropertyType.select === type) { let targetValue: any[] = defaultValue; targetValue = targetValue.filter(valItem => !!items.find(item => item.value === valItem)); this.propertyFormGroup.get('default').patchValue(targetValue, {emitEvent: false}); @@ -190,7 +280,7 @@ export class DynamicFormPropertyPanelComponent implements OnInit { } private onMultipleSelectChange() { - const type: FormPropertyType = this.propertyFormGroup.get('type').value; + const type = this.propertyItemType; const multiple: boolean = this.propertyFormGroup.get('multiple').value; const defaultValue: any = this.propertyFormGroup.get('default').value; if (type === FormPropertyType.select) { @@ -199,6 +289,8 @@ export class DynamicFormPropertyPanelComponent implements OnInit { const newVal = [defaultValue]; this.propertyFormGroup.get('default').patchValue(newVal, {emitEvent: false}); } + this.propertyFormGroup.get('allowEmptyOption').patchValue(false, {emitEvent: false}); + this.propertyFormGroup.get('allowEmptyOption').disable({emitEvent: false}); } else { if (defaultValue && Array.isArray(defaultValue)) { const newVal = defaultValue.length ? defaultValue[0] : null; @@ -206,6 +298,7 @@ export class DynamicFormPropertyPanelComponent implements OnInit { this.propertyFormGroup.get('default').patchValue(newVal, {emitEvent: false}); }); } + this.propertyFormGroup.get('allowEmptyOption').enable({emitEvent: false}); } } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-row.component.ts index c734c91fab..2d18684227 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-row.component.ts @@ -42,12 +42,12 @@ import { import { deepClone } from '@core/utils'; import { MatButton } from '@angular/material/button'; import { TbPopoverService } from '@shared/components/popover.service'; -import { constantColor, Font } from '@shared/models/widget-settings.models'; import { + defaultPropertyValue, FormProperty, FormPropertyType, formPropertyTypes, - formPropertyTypeTranslations + formPropertyTypeTranslations, propertyValid } from '@shared/models/dynamic-form.models'; import { DynamicFormPropertiesComponent @@ -57,39 +57,6 @@ import { } from '@home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -export const propertyValid = (property: FormProperty): boolean => !(!property.id || !property.name || !property.type); - -export const defaultPropertyValue = (type: FormPropertyType): any => { - switch (type) { - case FormPropertyType.text: - return ''; - case FormPropertyType.number: - return 0; - case FormPropertyType.switch: - return false; - case FormPropertyType.color: - return '#000'; - case FormPropertyType.color_settings: - return constantColor('#000'); - case FormPropertyType.font: - return { - size: 12, - sizeUnit: 'px', - family: 'Roboto', - weight: 'normal', - style: 'normal', - lineHeight: '1' - } as Font; - case FormPropertyType.units: - return ''; - case FormPropertyType.icon: - return 'star'; - case FormPropertyType.fieldset: - case FormPropertyType.select: - return null; - } -}; - @Component({ selector: 'tb-dynamic-form-property-row', templateUrl: './dynamic-form-property-row.component.html', @@ -168,7 +135,7 @@ export class DynamicFormPropertyRowComponent implements ControlValueAccessor, On this.propagateChange = fn; } - registerOnTouched(fn: any): void { + registerOnTouched(_fn: any): void { } setDisabledState(isDisabled: boolean): void { @@ -242,7 +209,7 @@ export class DynamicFormPropertyRowComponent implements ControlValueAccessor, On this.editProperty(null, this.editButton, true, onCanceled); } - public validate(c: UntypedFormControl) { + public validate(_c: UntypedFormControl) { const idControl = this.propertyRowFormGroup.get('id'); if (idControl.hasError('propertyIdNotUnique')) { idControl.updateValueAndValidity({onlySelf: false, emitEvent: false}); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-item-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-item-row.component.ts index 3e43c7a3cc..9db318f475 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-item-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-item-row.component.ts @@ -40,7 +40,6 @@ import { FormSelectItem } from '@shared/models/dynamic-form.models'; import { DynamicFormSelectItemsComponent } from '@home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-items.component'; -import { TimeSeriesChartStateSourceType } from '@home/components/widget/lib/chart/time-series-chart.models'; import { ValueType } from '@shared/models/constants'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @@ -105,7 +104,7 @@ export class DynamicFormSelectItemRowComponent implements ControlValueAccessor, this.propagateChange = fn; } - registerOnTouched(fn: any): void { + registerOnTouched(_fn: any): void { } setDisabledState(isDisabled: boolean): void { @@ -128,7 +127,7 @@ export class DynamicFormSelectItemRowComponent implements ControlValueAccessor, this.cd.markForCheck(); } - public validate(c: UntypedFormControl) { + public validate(_c: UntypedFormControl) { const valueControl = this.selectItemRowFormGroup.get('value'); if (valueControl.hasError('itemValueNotUnique')) { valueControl.updateValueAndValidity({onlySelf: false, emitEvent: false}); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-items.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-items.component.html index 03e1fdcbf5..64918ab339 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-items.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-items.component.html @@ -27,6 +27,7 @@ [cdkDropListDisabled]="!dragEnabled" (cdkDropListDropped)="selectItemDrop($event)">
) { const itemsArray = this.selectItemsFormGroup.get('selectItems') as UntypedFormArray; const item = itemsArray.at(event.previousIndex); - itemsArray.removeAt(event.previousIndex); - itemsArray.insert(event.currentIndex, item); + itemsArray.removeAt(event.previousIndex, {emitEvent: false}); + itemsArray.insert(event.currentIndex, item, {emitEvent: true}); } selectItemsFormArray(): UntypedFormArray { return this.selectItemsFormGroup.get('selectItems') as UntypedFormArray; } - trackBySelectItem(index: number, selectItemControl: AbstractControl): any { + trackBySelectItem(_index: number, selectItemControl: AbstractControl): any { return selectItemControl; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.html index 1acdcb6962..c617ef23d5 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.html @@ -15,7 +15,8 @@ limitations under the License. --> -
{{ title }}
@@ -42,19 +43,50 @@ - - + + + + + + + + + + + + +
+ + +
+ + +
+ +
- -
+ +
+ + +
+
+
{{ propertyRow.label | customTranslate }}
-
+
{{ property.subLabel | customTranslate }}
- - -
{{ property.fieldSuffix | customTranslate }}
-
- - - - {{ item.label | customTranslate }} - - - + + + + - - -
{{ property.fieldSuffix | customTranslate }}
-
@@ -115,3 +135,132 @@
+ + + + + {{ property.name | customTranslate }} + + + + {{ property.name | customTranslate }} + +
{{ property.fieldSuffix | customTranslate }}
+
+ + {{ property.name | customTranslate }} + +
{{ property.fieldSuffix | customTranslate }}
+
+ + {{ property.name | customTranslate }} + + + + {{ item.label | customTranslate }} + + + + + +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
{{ property.name | customTranslate }}
+ + +
{{ item.label | customTranslate }}
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.scss index ed4f7d837c..90354e904e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.scss @@ -13,10 +13,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + :host ::ng-deep { .tb-properties-content { gap: 16px; display: flex; flex-direction: column; } + .tb-form-row.tb-radios-property { + &.direction-column { + flex-direction: column; + align-items: flex-start; + gap: 4px; + } + .mat-mdc-radio-group { + overflow: hidden; + .mat-mdc-radio-button { + overflow: hidden; + .mdc-form-field { + overflow: hidden; + width: 100%; + .mdc-label { + overflow: hidden; + } + } + } + } + } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.ts index ac4348df16..3c9d7a1f83 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.ts @@ -44,11 +44,15 @@ import { FormPropertyContainerType, FormPropertyGroup, FormPropertyType, + isInputFieldPropertyType, PropertyConditionFunction, toPropertyGroups } from '@shared/models/dynamic-form.models'; import { coerceBoolean } from '@shared/decorators/coercion'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe'; +import { ContentType } from '@shared/models/constants'; +import { DomSanitizer } from '@angular/platform-browser'; @Component({ selector: 'tb-dynamic-form', @@ -69,10 +73,14 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; }) export class DynamicFormComponent implements OnInit, OnChanges, ControlValueAccessor, Validator { + isInputFieldPropertyType = isInputFieldPropertyType; + FormPropertyContainerType = FormPropertyContainerType; FormPropertyType = FormPropertyType; + ContentType = ContentType; + @Input() disabled: boolean; @@ -82,10 +90,22 @@ export class DynamicFormComponent implements OnInit, OnChanges, ControlValueAcce @Input() title: string; + @Input() + @coerceBoolean() + isArrayItem = false; + @Input() @coerceBoolean() stroked = false; + @Input() + @coerceBoolean() + noPadding = false; + + @Input() + @coerceBoolean() + noBorder = false; + private modelValue: {[id: string]: any}; private propagateChange = null; @@ -97,6 +117,8 @@ export class DynamicFormComponent implements OnInit, OnChanges, ControlValueAcce propertyGroups: FormPropertyGroup[]; constructor(protected store: Store, + private customTranslate: CustomTranslatePipe, + private sanitizer: DomSanitizer, private destroyRef: DestroyRef, private fb: UntypedFormBuilder, private cd: ChangeDetectorRef) { @@ -173,26 +195,28 @@ export class DynamicFormComponent implements OnInit, OnChanges, ControlValueAcce } } } - this.propertyGroups = toPropertyGroups(this.properties); + this.propertyGroups = toPropertyGroups(this.properties, this.isArrayItem, this.customTranslate, this.sanitizer); for (const property of this.properties) { - if (property.disableOnProperty) { - if (!this.validatorTriggers.includes(property.disableOnProperty)) { - this.validatorTriggers.push(property.disableOnProperty); + if (property.type !== FormPropertyType.htmlSection) { + if (property.disableOnProperty) { + if (!this.validatorTriggers.includes(property.disableOnProperty)) { + this.validatorTriggers.push(property.disableOnProperty); + } } - } - const validators: ValidatorFn[] = []; - if (property.required) { - validators.push(Validators.required); - } - if (property.type === FormPropertyType.number) { - if (isDefinedAndNotNull(property.min)) { - validators.push(Validators.min(property.min)); + const validators: ValidatorFn[] = []; + if (property.required) { + validators.push(Validators.required); } - if (isDefinedAndNotNull(property.max)) { - validators.push(Validators.max(property.max)); + if (property.type === FormPropertyType.number) { + if (isDefinedAndNotNull(property.min)) { + validators.push(Validators.min(property.min)); + } + if (isDefinedAndNotNull(property.max)) { + validators.push(Validators.max(property.max)); + } } + this.propertiesFormGroup.addControl(property.id, this.fb.control(null, validators), {emitEvent: false}); } - this.propertiesFormGroup.addControl(property.id, this.fb.control(null, validators), {emitEvent: false}); } } this.setupValue(); @@ -216,7 +240,7 @@ export class DynamicFormComponent implements OnInit, OnChanges, ControlValueAcce } this.propertyGroups.forEach(g => { g.containers.forEach(container => { - if (container.type === FormPropertyContainerType.fieldset) { + if ([FormPropertyContainerType.fieldset, FormPropertyContainerType.field, FormPropertyContainerType.htmlSection, FormPropertyContainerType.array].includes(container.type)) { container.visible = container.property.visible; } else { container.visible = container.switch?.visible || container.properties.some(p => p.visible); @@ -233,12 +257,14 @@ export class DynamicFormComponent implements OnInit, OnChanges, ControlValueAcce private updateControlsState() { if (this.properties) { for (let property of this.properties) { - const control = this.propertiesFormGroup.get(property.id); - if (property.visible && !property.disabled) { - control.enable({emitEvent: false}); - control.updateValueAndValidity({emitEvent: false}); - } else { - control.disable({emitEvent: false}); + if (property.type !== FormPropertyType.htmlSection) { + const control = this.propertiesFormGroup.get(property.id); + if (property.visible && !property.disabled) { + control.enable({emitEvent: false}); + control.updateValueAndValidity({emitEvent: false}); + } else { + control.disable({emitEvent: false}); + } } } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts index 1fc32f8c67..627c0b6362 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts @@ -180,6 +180,9 @@ import { import { DynamicFormSelectItemRowComponent } from '@home/components/widget/lib/settings/common/dynamic-form/dynamic-form-select-item-row.component'; +import { + DynamicFormArrayComponent +} from '@home/components/widget/lib/settings/common/dynamic-form/dynamic-form-array.component'; @NgModule({ declarations: [ @@ -248,7 +251,8 @@ import { DynamicFormPropertyPanelComponent, DynamicFormSelectItemsComponent, DynamicFormSelectItemRowComponent, - DynamicFormComponent + DynamicFormComponent, + DynamicFormArrayComponent ], imports: [ CommonModule, @@ -321,7 +325,8 @@ import { DynamicFormPropertyPanelComponent, DynamicFormSelectItemsComponent, DynamicFormSelectItemRowComponent, - DynamicFormComponent + DynamicFormComponent, + DynamicFormArrayComponent ], providers: [ ColorSettingsComponentService, diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts index 6e837b0740..648491faed 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts @@ -112,6 +112,7 @@ export class WidgetComponentService { templateHtml: this.utils.editWidgetInfo.templateHtml, templateCss: this.utils.editWidgetInfo.templateCss, controllerScript: this.utils.editWidgetInfo.controllerScript, + settingsForm: this.utils.editWidgetInfo.settingsForm, settingsSchema: this.utils.editWidgetInfo.settingsSchema, dataKeySettingsSchema: this.utils.editWidgetInfo.dataKeySettingsSchema, latestDataKeySettingsSchema: this.utils.editWidgetInfo.latestDataKeySettingsSchema, @@ -298,6 +299,9 @@ export class WidgetComponentService { this.loadWidgetResources(widgetInfo, widgetNamespace, [SharedModule, WidgetComponentsModule, this.homeComponentsModule]).subscribe( { next: () => { + if (widgetControllerDescriptor.settingsForm) { + widgetInfo.typeSettingsForm = widgetControllerDescriptor.settingsForm; + } if (widgetControllerDescriptor.settingsSchema) { widgetInfo.typeSettingsSchema = widgetControllerDescriptor.settingsSchema; } @@ -539,6 +543,9 @@ export class WidgetComponentService { const result: WidgetControllerDescriptor = { widgetTypeFunction: widgetType }; + if (isFunction(widgetTypeInstance.getSettingsForm)) { + result.settingsForm = widgetTypeInstance.getSettingsForm(); + } if (isFunction(widgetTypeInstance.getSettingsSchema)) { result.settingsSchema = widgetTypeInstance.getSettingsSchema(); } diff --git a/ui-ngx/src/app/modules/home/models/widget-component.models.ts b/ui-ngx/src/app/modules/home/models/widget-component.models.ts index 31583747bc..8de7dbfb8b 100644 --- a/ui-ngx/src/app/modules/home/models/widget-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/widget-component.models.ts @@ -561,6 +561,7 @@ export interface WidgetInfo extends WidgetTypeDescriptor, WidgetControllerDescri fullFqn: string; deprecated: boolean; scada: boolean; + typeSettingsForm?: FormProperty[]; typeSettingsSchema?: string | any; typeDataKeySettingsSchema?: string | any; typeLatestDataKeySettingsSchema?: string | any; @@ -644,6 +645,7 @@ export const ErrorWidgetType: WidgetInfo = { }; export interface WidgetTypeInstance { + getSettingsForm?: () => FormProperty[]; getSettingsSchema?: () => string; getDataKeySettingsSchema?: () => string; getLatestDataKeySettingsSchema?: () => string; @@ -672,6 +674,7 @@ export const toWidgetInfo = (widgetTypeEntity: WidgetType): WidgetInfo => ({ templateHtml: widgetTypeEntity.descriptor.templateHtml, templateCss: widgetTypeEntity.descriptor.templateCss, controllerScript: widgetTypeEntity.descriptor.controllerScript, + settingsForm: widgetTypeEntity.descriptor.settingsForm, settingsSchema: widgetTypeEntity.descriptor.settingsSchema, dataKeySettingsSchema: widgetTypeEntity.descriptor.dataKeySettingsSchema, latestDataKeySettingsSchema: widgetTypeEntity.descriptor.latestDataKeySettingsSchema, @@ -701,6 +704,7 @@ export const toWidgetType = (widgetInfo: WidgetInfo, id: WidgetTypeId, tenantId: templateHtml: widgetInfo.templateHtml, templateCss: widgetInfo.templateCss, controllerScript: widgetInfo.controllerScript, + settingsForm: widgetInfo.settingsForm, settingsSchema: widgetInfo.settingsSchema, dataKeySettingsSchema: widgetInfo.dataKeySettingsSchema, latestDataKeySettingsSchema: widgetInfo.latestDataKeySettingsSchema, diff --git a/ui-ngx/src/app/modules/home/pages/entity-view/entity-view.component.html b/ui-ngx/src/app/modules/home/pages/entity-view/entity-view.component.html index c0ee9b61b7..4cc1f0fedc 100644 --- a/ui-ngx/src/app/modules/home/pages/entity-view/entity-view.component.html +++ b/ui-ngx/src/app/modules/home/pages/entity-view/entity-view.component.html @@ -148,17 +148,15 @@
- -
diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-behaviors.component.html b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-behaviors.component.html index d566336057..fc02871a9c 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-behaviors.component.html +++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-behaviors.component.html @@ -28,6 +28,7 @@ [cdkDropListDisabled]="!dragEnabled" (cdkDropListDropped)="behaviorDrop($event)">
) { const behaviorsArray = this.behaviorsFormGroup.get('behaviors') as UntypedFormArray; const behavior = behaviorsArray.at(event.previousIndex); - behaviorsArray.removeAt(event.previousIndex); - behaviorsArray.insert(event.currentIndex, behavior); + behaviorsArray.removeAt(event.previousIndex, {emitEvent: false}); + behaviorsArray.insert(event.currentIndex, behavior, {emitEvent: true}); } behaviorsFormArray(): UntypedFormArray { diff --git a/ui-ngx/src/app/shared/components/js-func.component.ts b/ui-ngx/src/app/shared/components/js-func.component.ts index 647ab6113d..8e500bdbe9 100644 --- a/ui-ngx/src/app/shared/components/js-func.component.ts +++ b/ui-ngx/src/app/shared/components/js-func.component.ts @@ -80,6 +80,8 @@ export class JsFuncComponent implements OnInit, OnDestroy, ControlValueAccessor, toastTargetId = `jsFuncEditor-${guid()}`; + @Input() label: string; + @Input() functionTitle: string; @Input() functionName: string; @@ -102,7 +104,9 @@ export class JsFuncComponent implements OnInit, OnDestroy, ControlValueAccessor, @Input() globalVariables: Array; - @Input() disableUndefinedCheck = false; + @Input() + @coerceBoolean() + disableUndefinedCheck = false; @Input() helpId: string; @@ -174,7 +178,7 @@ export class JsFuncComponent implements OnInit, OnDestroy, ControlValueAccessor, } ngOnInit(): void { - if (this.functionTitle) { + if (this.functionTitle || this.label) { this.hideBrackets = true; } if (!this.resultType || this.resultType.length === 0) { @@ -190,6 +194,8 @@ export class JsFuncComponent implements OnInit, OnDestroy, ControlValueAccessor, } if (this.functionTitle) { this.functionLabel = `${this.functionTitle}: f(${this.functionArgsString})`; + } else if (this.label) { + this.functionLabel = this.label; } else { this.functionLabel = `function ${this.functionName ? this.functionName : ''}(${this.functionArgsString})${this.hideBrackets ? '' : ' {'}`; diff --git a/ui-ngx/src/app/shared/components/string-items-list.component.html b/ui-ngx/src/app/shared/components/string-items-list.component.html index 375f22e02f..eb4c15fc18 100644 --- a/ui-ngx/src/app/shared/components/string-items-list.component.html +++ b/ui-ngx/src/app/shared/components/string-items-list.component.html @@ -15,7 +15,8 @@ limitations under the License. --> - diff --git a/ui-ngx/src/app/shared/components/string-items-list.component.ts b/ui-ngx/src/app/shared/components/string-items-list.component.ts index 951abaa12b..4121a7e8d7 100644 --- a/ui-ngx/src/app/shared/components/string-items-list.component.ts +++ b/ui-ngx/src/app/shared/components/string-items-list.component.ts @@ -108,6 +108,9 @@ export class StringItemsListComponent implements ControlValueAccessor, OnInit { @Input() subscriptSizing: SubscriptSizing = 'fixed'; + @Input() + fieldClass: string; + @Input() @coerceArray() predefinedValues: StringItemsOption[]; diff --git a/ui-ngx/src/app/shared/components/time/datetime.component.html b/ui-ngx/src/app/shared/components/time/datetime.component.html index edf59702f1..f0965deaf8 100644 --- a/ui-ngx/src/app/shared/components/time/datetime.component.html +++ b/ui-ngx/src/app/shared/components/time/datetime.component.html @@ -15,26 +15,26 @@ limitations under the License. --> -
- - {{ dateText | translate }} - - - - - - {{ timeText | translate }} - - - - -
+ + {{ dateText }} +
+ + +
+ + +
diff --git a/ui-ngx/src/app/shared/components/time/datetime.component.scss b/ui-ngx/src/app/shared/components/time/datetime.component.scss new file mode 100644 index 0000000000..b3333d34d5 --- /dev/null +++ b/ui-ngx/src/app/shared/components/time/datetime.component.scss @@ -0,0 +1,60 @@ +/** + * 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 '../../../../scss/constants'; + +:host-context(.tb-form-row) { + :host { + display: flex; + } + .mat-mdc-form-field { + &.tb-datetime-field { + width: 185px; + &.with-clear { + width: 225px; + } + } + &.tb-date-field { + width: 140px; + &.with-clear { + width: 180px; + } + } + &.tb-time-field { + width: 100px; + &.with-clear { + width: 140px; + } + } + } +} + +:host-context(.tb-form-row.column-xs) { + @media #{$mat-xs} { + .mat-mdc-form-field { + &.tb-datetime-field, &.tb-date-field, &.tb-time-field { + width: auto; + flex: 1; + } + } + } +} + +:host { + .mat-mdc-form-field-icon-suffix, + .mat-datetimepicker-toggle { + color: rgba(0, 0, 0, 0.38); + } +} diff --git a/ui-ngx/src/app/shared/components/time/datetime.component.ts b/ui-ngx/src/app/shared/components/time/datetime.component.ts index c004ab3786..52029fb958 100644 --- a/ui-ngx/src/app/shared/components/time/datetime.component.ts +++ b/ui-ngx/src/app/shared/components/time/datetime.component.ts @@ -17,11 +17,14 @@ import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { FloatLabelType, MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field'; +import { MatDatetimepickerType } from '@mat-datetimepicker/core/datetimepicker/datetimepicker-type'; +import { coerceBoolean } from '@shared/decorators/coercion'; @Component({ selector: 'tb-datetime', templateUrl: './datetime.component.html', - styleUrls: [], + styleUrls: ['./datetime.component.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, @@ -41,6 +44,18 @@ export class DatetimeComponent implements OnInit, ControlValueAccessor { this.requiredValue = coerceBooleanProperty(value); } + @Input() + floatLabel: FloatLabelType = 'auto'; + + @Input() + subscriptSizing: SubscriptSizing = 'fixed'; + + @Input() + appearance: MatFormFieldAppearance = 'fill'; + + @Input() + type: MatDatetimepickerType = 'datetime'; + @Input() disabled: boolean; @@ -48,10 +63,15 @@ export class DatetimeComponent implements OnInit, ControlValueAccessor { dateText: string; @Input() - timeText: string; + @coerceBoolean() + showLabel = true; @Input() - showLabel = true; + @coerceBoolean() + allowClear = false; + + @Input() + fieldClass: string; minDateValue: Date | null; @@ -111,4 +131,10 @@ export class DatetimeComponent implements OnInit, ControlValueAccessor { this.updateView(value); } + clear($event: Event) { + $event.stopPropagation(); + this.date = null; + this.updateView(null); + } + } diff --git a/ui-ngx/src/app/shared/models/dynamic-form.models.ts b/ui-ngx/src/app/shared/models/dynamic-form.models.ts index 70c70b42b5..773f6bdebe 100644 --- a/ui-ngx/src/app/shared/models/dynamic-form.models.ts +++ b/ui-ngx/src/app/shared/models/dynamic-form.models.ts @@ -16,22 +16,36 @@ import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe'; import { TbEditorCompletion, TbEditorCompletions } from '@shared/models/ace/completion.models'; -import { isString } from '@core/utils'; +import { deepClone, isDefinedAndNotNull, isString } from '@core/utils'; import { JsonSchema, JsonSettingsSchema } from '@shared/models/widget.models'; import JsonFormUtils from '@shared/components/json-form/react/json-form-utils'; import { JsonFormData, KeyLabelItem } from '@shared/components/json-form/react/json-form.models'; +import { constantColor, Font } from '@shared/models/widget-settings.models'; +import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; export enum FormPropertyType { text = 'text', number = 'number', + password = 'password', + textarea = 'textarea', switch = 'switch', select = 'select', + radios = 'radios', + datetime = 'datetime', + image = 'image', + javascript = 'javascript', + json = 'json', + html = 'html', + css = 'css', + markdown = 'markdown', color = 'color', color_settings = 'color_settings', font = 'font', units = 'units', icon = 'icon', - fieldset = 'fieldset' + fieldset = 'fieldset', + array = 'array', + htmlSection = 'htmlSection' } export const formPropertyTypes = Object.keys(FormPropertyType) as FormPropertyType[]; @@ -40,14 +54,26 @@ export const formPropertyTypeTranslations = new Map( [ [FormPropertyType.text, 'dynamic-form.property.type-text'], [FormPropertyType.number, 'dynamic-form.property.type-number'], + [FormPropertyType.password, 'dynamic-form.property.type-password'], + [FormPropertyType.textarea, 'dynamic-form.property.type-textarea'], [FormPropertyType.switch, 'dynamic-form.property.type-switch'], [FormPropertyType.select, 'dynamic-form.property.type-select'], + [FormPropertyType.radios, 'dynamic-form.property.type-radios'], + [FormPropertyType.datetime, 'dynamic-form.property.type-datetime'], + [FormPropertyType.image, 'dynamic-form.property.type-image'], + [FormPropertyType.javascript, 'dynamic-form.property.type-javascript'], + [FormPropertyType.json, 'dynamic-form.property.type-json'], + [FormPropertyType.html, 'dynamic-form.property.type-html'], + [FormPropertyType.css, 'dynamic-form.property.type-css'], + [FormPropertyType.markdown, 'dynamic-form.property.type-markdown'], [FormPropertyType.color, 'dynamic-form.property.type-color'], [FormPropertyType.color_settings, 'dynamic-form.property.type-color-settings'], [FormPropertyType.font, 'dynamic-form.property.type-font'], [FormPropertyType.units, 'dynamic-form.property.type-units'], [FormPropertyType.icon, 'dynamic-form.property.type-icon'], - [FormPropertyType.fieldset, 'dynamic-form.property.type-fieldset'] + [FormPropertyType.fieldset, 'dynamic-form.property.type-fieldset'], + [FormPropertyType.array, 'dynamic-form.property.type-array'], + [FormPropertyType.htmlSection, 'dynamic-form.property.type-html-section'] ] ); @@ -78,6 +104,10 @@ export interface FormPropertyBase { fieldClass?: string; } +export interface FormTextareaProperty extends FormPropertyBase { + rows?: number; +} + export interface FormNumberProperty extends FormPropertyBase { min?: number; max?: number; @@ -88,6 +118,11 @@ export interface FormFieldSetProperty extends FormPropertyBase { properties?: FormProperty[]; } +export interface FormArrayProperty extends FormPropertyBase { + arrayItemName?: string; + arrayItemType?: FormPropertyType; +} + export interface FormSelectItem { value: any; label: string; @@ -95,33 +130,82 @@ export interface FormSelectItem { export interface FormSelectProperty extends FormPropertyBase { multiple?: boolean; + allowEmptyOption?: boolean; items?: FormSelectItem[]; } -export type FormProperty = FormPropertyBase & FormNumberProperty & FormSelectProperty & FormFieldSetProperty; +export type FormPropertyDirection = 'row' | 'column'; + +export interface FormRadiosProperty extends FormPropertyBase { + direction?: FormPropertyDirection; + items?: FormSelectItem[]; +} + +export type FormPropertyDateTimeType = 'date' | 'time' | 'datetime'; + +export interface FormDateTimeProperty extends FormPropertyBase { + allowClear?: boolean; + dateTimeType?: FormPropertyDateTimeType; +} + +export interface FormJavascriptProperty extends FormPropertyBase { + helpId?: string; +} + +export interface FormMarkdownProperty extends FormPropertyBase { + helpId?: string; +} + +export interface FormHtmlSection extends FormPropertyBase { + htmlClassList?: string[]; + htmlContent?: string; +} + +export type FormProperty = FormPropertyBase & FormTextareaProperty & FormNumberProperty & FormSelectProperty & FormRadiosProperty + & FormDateTimeProperty & FormJavascriptProperty & FormMarkdownProperty & FormFieldSetProperty & FormArrayProperty & FormHtmlSection; export enum FormPropertyContainerType { + field = 'field', row = 'row', - fieldset = 'fieldset' + fieldset = 'fieldset', + array = 'array', + htmlSection = 'htmlSection' } export interface FormPropertyContainerBase { type: FormPropertyContainerType; label: string; - properties: FormProperty[]; visible: boolean; } export interface FormPropertyRow extends FormPropertyContainerBase { + properties?: FormProperty[]; switch?: FormProperty; rowClass?: string; + propertiesRowClass?: string; +} + +export interface FormPropertyField extends FormPropertyContainerBase { + property?: FormProperty; } export interface FormPropertyFieldset extends FormPropertyContainerBase { property?: FormProperty; + properties?: FormProperty[]; } -export type FormPropertyContainer = FormPropertyRow & FormPropertyFieldset; +export interface FormPropertyArray extends FormPropertyContainerBase { + property?: FormProperty; + arrayItemProperty?: FormProperty; +} + +export interface FormPropertyHtml extends FormPropertyContainerBase { + property?: FormProperty; + safeHtml?: SafeHtml; + htmlClass?: string; +} + +export type FormPropertyContainer = FormPropertyField & FormPropertyRow & FormPropertyFieldset & FormPropertyHtml; export interface FormPropertyGroup { title?: string; @@ -129,14 +213,22 @@ export interface FormPropertyGroup { visible: boolean; } -export const toPropertyGroups = (properties: FormProperty[]): FormPropertyGroup[] => { +export const toPropertyGroups = (properties: FormProperty[], + isArrayItem: boolean, + customTranslate: CustomTranslatePipe, + sanitizer: DomSanitizer): FormPropertyGroup[] => { const groups: {title: string, properties: FormProperty[]}[] = []; for (let property of properties) { if (!property.group) { - groups.push({ - title: null, - properties: [property] - }); + let group = groups.length ? groups[groups.length - 1] : null; + if (group && !group.title) { + group.properties.push(property); + } else { + groups.push({ + title: null, + properties: [property] + }); + } } else { let propertyGroup = groups.find(g => g.title === property.group); if (!propertyGroup) { @@ -151,15 +243,35 @@ export const toPropertyGroups = (properties: FormProperty[]): FormPropertyGroup[ } return groups.map(g => ({ title: g.title, - containers: toPropertyContainers(g.properties), + containers: toPropertyContainers(g.properties, isArrayItem, customTranslate, sanitizer), visible: true })); }; -const toPropertyContainers = (properties: FormProperty[]): FormPropertyContainer[] => { +const toPropertyContainers = (properties: FormProperty[], + isArrayItem: boolean, + customTranslate: CustomTranslatePipe, + sanitizer: DomSanitizer): FormPropertyContainer[] => { const result: FormPropertyContainer[] = []; for (let property of properties) { - if (property.type === FormPropertyType.fieldset) { + if (property.type === FormPropertyType.array) { + const propertyArray: FormPropertyArray = { + property, + label: property.name, + type: FormPropertyContainerType.array, + visible: true + }; + const arrayItemProperty = deepClone(property); + arrayItemProperty.name = property.arrayItemName; + arrayItemProperty.type = property.arrayItemType; + arrayItemProperty.required = true; + delete arrayItemProperty.disableOnProperty; + delete arrayItemProperty.condition; + delete arrayItemProperty.conditionFunction; + delete arrayItemProperty.group; + propertyArray.arrayItemProperty = arrayItemProperty; + result.push(propertyArray); + } else if (property.type === FormPropertyType.fieldset) { const propertyFieldset: FormPropertyFieldset = { property, label: property.name, @@ -168,6 +280,24 @@ const toPropertyContainers = (properties: FormProperty[]): FormPropertyContainer visible: true }; result.push(propertyFieldset); + } else if (property.type === FormPropertyType.htmlSection) { + const propertyHtml: FormPropertyHtml = { + property, + label: property.name, + type: FormPropertyContainerType.htmlSection, + htmlClass: property.htmlClassList ? property.htmlClassList.join(' ') : '', + safeHtml: sanitizer.bypassSecurityTrustHtml(property.htmlContent), + visible: true + }; + result.push(propertyHtml); + } else if (isSingleFieldPropertyType(property.type) || isArrayItem) { + const propertyField: FormPropertyField = { + property, + label: property.name, + type: FormPropertyContainerType.field, + visible: true + }; + result.push(propertyField); } else { let propertyRow = result.find(r => r.type === FormPropertyContainerType.row && r.label === property.name); @@ -177,6 +307,7 @@ const toPropertyContainers = (properties: FormProperty[]): FormPropertyContainer type: FormPropertyContainerType.row, properties: [], rowClass: property.rowClass, + propertiesRowClass: 'row flex-end align-center', visible: true }; result.push(propertyRow); @@ -188,22 +319,60 @@ const toPropertyContainers = (properties: FormProperty[]): FormPropertyContainer } } } + for (let container of result.filter(c => + c.type === FormPropertyContainerType.row && !c.switch && c.properties?.length === 1)) { + const property = container.properties[0]; + if (isInputFieldPropertyType(property.type)) { + const labelText = customTranslate.transform(property.name); + if (property.type !== FormPropertyType.number && labelText.length > 40) { + container.type = FormPropertyContainerType.field; + container.property = property; + delete container.properties; + delete container.rowClass; + } else { + if (!container.rowClass) { + container.rowClass = 'column-xs'; + container.propertiesRowClass = 'gt-xs:align-center xs:flex-col gt-xs:flex-row gt-xs:justify-end'; + } + } + } + } return result; } +export const isPropertyTypeAllowedForRow = (type: FormPropertyType): boolean => { + return !isSingleFieldPropertyType(type) && ![FormPropertyType.fieldset, FormPropertyType.array, FormPropertyType.htmlSection].includes(type); +} + +export const isSingleFieldPropertyType = (type: FormPropertyType): boolean => { + return [FormPropertyType.radios, FormPropertyType.textarea, FormPropertyType.image, FormPropertyType.javascript, FormPropertyType.json, FormPropertyType.html, + FormPropertyType.css, FormPropertyType.markdown].includes(type); +} + +export const isInputFieldPropertyType = (type: FormPropertyType): boolean => { + return [FormPropertyType.text, FormPropertyType.password, FormPropertyType.number, FormPropertyType.select, FormPropertyType.datetime, + FormPropertyType.textarea].includes(type); +} + export const defaultFormProperties = (properties: FormProperty[]): {[id: string]: any} => { const formProperties: {[id: string]: any} = {}; for (const property of properties) { - formProperties[property.id] = defaultFormPropertyValue(property); + if (property.type !== FormPropertyType.htmlSection) { + formProperties[property.id] = defaultFormPropertyValue(property); + } } return formProperties; }; export const defaultFormPropertyValue = (property: FormProperty): any => { - if (property.type === FormPropertyType.fieldset) { + if (property.type === FormPropertyType.array) { + return []; + } else if (property.type === FormPropertyType.fieldset) { const propertyValue: {[id: string]: any} = {}; for (const childProperty of property.properties) { - propertyValue[childProperty.id] = defaultFormPropertyValue(childProperty); + if (childProperty.type !== FormPropertyType.htmlSection) { + propertyValue[childProperty.id] = defaultFormPropertyValue(childProperty); + } } return propertyValue; } else { @@ -211,10 +380,58 @@ export const defaultFormPropertyValue = (property: FormProperty): any => { } } +export const propertyValid = (property: FormProperty): boolean => + !(!property.id || !property.name || !property.type || (property.type === FormPropertyType.array && !property.arrayItemType)); + +export const defaultPropertyValue = (type: FormPropertyType): any => { + switch (type) { + case FormPropertyType.text: + case FormPropertyType.textarea: + case FormPropertyType.password: + case FormPropertyType.javascript: + case FormPropertyType.json: + case FormPropertyType.html: + case FormPropertyType.css: + case FormPropertyType.markdown: + return ''; + case FormPropertyType.number: + return 0; + case FormPropertyType.switch: + return false; + case FormPropertyType.color: + return '#000'; + case FormPropertyType.color_settings: + return constantColor('#000'); + case FormPropertyType.font: + return { + size: 12, + sizeUnit: 'px', + family: 'Roboto', + weight: 'normal', + style: 'normal', + lineHeight: '1' + } as Font; + case FormPropertyType.units: + return ''; + case FormPropertyType.icon: + return 'star'; + case FormPropertyType.fieldset: + case FormPropertyType.array: + case FormPropertyType.select: + case FormPropertyType.radios: + case FormPropertyType.datetime: + case FormPropertyType.htmlSection: + case FormPropertyType.image: + return null; + } +}; + export const formPropertyCompletions = (properties: FormProperty[], customTranslate: CustomTranslatePipe): TbEditorCompletions => { const propertiesCompletions: TbEditorCompletions = {}; for (const property of properties) { - propertiesCompletions[property.id] = formPropertyCompletion(property, customTranslate); + if (property.type !== FormPropertyType.htmlSection) { + propertiesCompletions[property.id] = formPropertyCompletion(property, customTranslate); + } } return propertiesCompletions; } @@ -224,37 +441,63 @@ export const formPropertyCompletion = (property: FormProperty, customTranslate: if (property.subLabel) { description += ` ${customTranslate.transform(property.subLabel, property.subLabel)}`; } - if (property.type === FormPropertyType.select) { - if (property.multiple) { + const isArray = property.type === FormPropertyType.array; + const type = isArray ? property.arrayItemType : property.type; + if (type === FormPropertyType.select) { + if (property.multiple || isArray) { description += '

Possible values of array element:'; } else { description += '

Possible values:'; } description += `
    ${property.items.map(item => `
  • ${item.value} ${typeof item.value}
  • `).join('\n')}
`; } + if (type === FormPropertyType.datetime) { + if (isArray) { + description += '

Stores array of time values in milliseconds since midnight, January 1, 1970 UTC.'; + } else { + description += '

Stores time value in milliseconds since midnight, January 1, 1970 UTC.'; + } + } const completion: TbEditorCompletion = { meta: 'property', description, type: formPropertyCompletionType(property) }; - if (property.type === FormPropertyType.fieldset) { + if (type === FormPropertyType.fieldset && !isArray) { completion.children = {}; for (const childProperty of property.properties) { - completion.children[childProperty.id] = formPropertyCompletion(childProperty, customTranslate); + if (childProperty.type !== FormPropertyType.htmlSection) { + completion.children[childProperty.id] = formPropertyCompletion(childProperty, customTranslate); + } } } return completion; }; const formPropertyCompletionType = (property: FormProperty): string => { - switch (property.type) { + const isArray = property.type === FormPropertyType.array; + const type = isArray ? property.arrayItemType : property.type; + let typeStr: string; + switch (type) { case FormPropertyType.text: - return 'string'; + case FormPropertyType.password: + case FormPropertyType.textarea: + typeStr = 'string'; + break; case FormPropertyType.number: - return 'number'; + typeStr = 'number'; + break; case FormPropertyType.switch: - return 'boolean'; + typeStr = 'boolean'; + break; + case FormPropertyType.datetime: + typeStr = 'number'; + break; + case FormPropertyType.image: + typeStr = 'image URL string'; + break; case FormPropertyType.select: + case FormPropertyType.radios: const items = property.items || []; const types: string[] = []; items.forEach(item => { @@ -264,24 +507,53 @@ const formPropertyCompletionType = (property: FormProperty): string => { } }); const typesString = types.length ? types.join(' | ') : 'string'; - if (property.multiple) { - return `Array<${typesString}>`; + if (property.type === FormPropertyType.select && property.multiple) { + typeStr = `Array<${typesString}>`; } else { - return typesString; + typeStr = typesString; } + break; case FormPropertyType.color: - return 'color string'; + typeStr = 'color string'; + break; case FormPropertyType.color_settings: - return 'ColorProcessor'; + typeStr = 'ColorProcessor'; + break; case FormPropertyType.font: - return 'Font'; + typeStr = 'Font'; + break; case FormPropertyType.units: - return 'units string'; + typeStr = 'units string'; + break; case FormPropertyType.icon: - return 'icon string'; + typeStr = 'icon string'; + break; case FormPropertyType.fieldset: - return 'object'; + typeStr = 'object'; + break; + case FormPropertyType.javascript: + typeStr = 'JavaScript function body string'; + break; + case FormPropertyType.json: + typeStr = 'JSON string'; + break; + case FormPropertyType.html: + typeStr = 'HTML string'; + break; + case FormPropertyType.css: + typeStr = 'CSS string'; + break; + case FormPropertyType.markdown: + typeStr = 'Markdown string'; + break; + default: + typeStr = 'unknown'; + break; + } + if (isArray) { + typeStr = `Array<${typeStr}>`; } + return typeStr; }; @@ -322,13 +594,14 @@ const schemaFormToProperties = (schema: JsonSchema, theForm: any[], groupTitle?: const jsonFormDataToProperty = (form: JsonFormData, level: number, groupTitle?: string): FormProperty => { if (form.key && form.key.length > level) { - const property: FormProperty = { - id: form.key[level] + '', - name: form.title, + const id = form.key[level] + ''; + let property: FormProperty = { + id, + name: form.title || id, group: groupTitle, type: null, - default: form.schema.default, - required: form.required + default: form.schema?.default || null, + required: isDefinedAndNotNull(form.required) ? form.required : false }; if (form.condition?.length) { property.condition = `return ${form.condition};`; @@ -341,6 +614,14 @@ const jsonFormDataToProperty = (form: JsonFormData, level: number, groupTitle?: property.type = FormPropertyType.text; property.fieldClass = 'flex'; break; + case 'password': + property.type = FormPropertyType.password; + property.fieldClass = 'flex'; + break; + case 'textarea': + property.type = FormPropertyType.textarea; + property.rows = form.rows || form.rowsMax || 2; + break; case 'checkbox': property.type = FormPropertyType.switch; break; @@ -353,6 +634,7 @@ const jsonFormDataToProperty = (form: JsonFormData, level: number, groupTitle?: } property.multiple = form.multiple; property.fieldClass = 'flex'; + property.allowEmptyOption = isDefinedAndNotNull(form.allowClear) ? form.allowClear : false; break; case 'select': property.type = FormPropertyType.select; @@ -363,6 +645,25 @@ const jsonFormDataToProperty = (form: JsonFormData, level: number, groupTitle?: } property.multiple = false; property.fieldClass = 'flex'; + property.allowEmptyOption = false; + break; + case 'radios': + property.type = FormPropertyType.radios; + if (form.titleMap?.length) { + property.items = form.titleMap.map(item => ({value: item.value, label: item.name})); + } else { + property.items = []; + } + property.direction = form.direction === 'row' ? 'row' : 'column'; + break; + case 'date': + property.type = FormPropertyType.datetime; + property.dateTimeType = 'date'; + property.fieldClass = 'flex'; + property.allowClear = true; + break; + case 'image': + property.type = FormPropertyType.image; break; case 'color': property.type = FormPropertyType.color; @@ -375,6 +676,44 @@ const jsonFormDataToProperty = (form: JsonFormData, level: number, groupTitle?: property.properties = form.items ? (form.items as JsonFormData[]).map(item => jsonFormDataToProperty(item, level+1)).filter(p => p !== null) : []; break; + case 'javascript': + property.type = FormPropertyType.javascript; + property.helpId = form.helpId; + break; + case 'json': + property.type = FormPropertyType.json; + break; + case 'html': + property.type = FormPropertyType.html; + break; + case 'css': + property.type = FormPropertyType.css; + break; + case 'markdown': + property.type = FormPropertyType.markdown; + break; + case 'help': + property.type = FormPropertyType.htmlSection; + property.htmlContent = form.description || ''; + property.htmlClassList = form.htmlClass ? form.htmlClass.split(' ') : []; + break; + case 'array': + if (form.items?.length) { + const item: JsonFormData = form.items[0] as JsonFormData; + if (item.type !== 'array') { + const arrayProperty = jsonFormDataToProperty(item, 0); + arrayProperty.arrayItemType = arrayProperty.type; + arrayProperty.arrayItemName = arrayProperty.name; + arrayProperty.id = property.id; + arrayProperty.name = property.name; + arrayProperty.group = property.group; + arrayProperty.condition = property.condition; + arrayProperty.required = property.required; + property = arrayProperty; + property.type = FormPropertyType.array; + } + } + break; } if (!property.type) { return null; diff --git a/ui-ngx/src/app/shared/models/widget.models.ts b/ui-ngx/src/app/shared/models/widget.models.ts index 6f5579c77f..f3c82571e1 100644 --- a/ui-ngx/src/app/shared/models/widget.models.ts +++ b/ui-ngx/src/app/shared/models/widget.models.ts @@ -195,6 +195,7 @@ export interface WidgetTypeParameters { export interface WidgetControllerDescriptor { widgetTypeFunction?: any; + settingsForm?: FormProperty[]; settingsSchema?: string | any; dataKeySettingsSchema?: string | any; latestDataKeySettingsSchema?: string | any; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index c43ff76da3..58e947e80e 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1625,15 +1625,27 @@ "name": "Name", "type": "Type", "type-text": "Text", + "type-password": "Password", + "type-textarea": "Text area", "type-number": "Number", "type-switch": "Switch", "type-select": "Select", + "type-radios": "Radio buttons", + "type-datetime": "Date/Time", + "type-image": "Image", + "type-javascript": "JavaScript", + "type-json": "JSON", + "type-html": "HTML", + "type-css": "CSS", + "type-markdown": "Markdown", "type-color": "Color", "type-color-settings": "Color settings", "type-font": "Font", "type-units": "Units", "type-icon": "Icon", "type-fieldset": "Fieldset", + "type-array": "Array", + "type-html-section": "HTML section", "group-title": "Group title", "no-properties": "No properties configured", "add-property": "Add property", @@ -1655,13 +1667,32 @@ "property-field-classes": "Property field classes", "not-unique-property-ids-error": "Property Ids must be unique!", "enable-multiple-select": "Enable multiple select", + "allow-empty-select-option": "Allow empty option", "select-options": "Select options", "not-unique-select-option-value-error": "Select option values must be unique!", "value": "Value", "label": "Label", "add-option": "Add option", "no-options": "No options configured", - "remove-option": "Remove option" + "remove-option": "Remove option", + "textarea-rows": "Textarea rows", + "help-id": "Help id", + "buttons-direction": "Buttons direction", + "direction-row": "Row", + "direction-column": "Column", + "radio-button-options": "Radio button options", + "datetime-type": "Date/Time field type", + "datetime-type-date": "Date", + "datetime-type-time": "Time", + "datetime-type-datetime": "Date/Time", + "enable-clear-button": "Enable clear button", + "html-section-settings": "HTML section settings", + "html-section-classes": "HTML section classes", + "html-section-content": "HTML section content", + "array-item": "Array item", + "item-type": "Item type", + "item-name": "Item name", + "no-items": "No items" } }, "asset-profile": { @@ -2548,9 +2579,7 @@ "select-entity-view": "Select entity view", "make-public": "Make entity view public", "make-private": "Make entity view private", - "start-date": "Start date", "start-ts": "Start time", - "end-date": "End date", "end-ts": "End time", "date-limits": "Date limits", "client-attributes": "Client attributes", diff --git a/ui-ngx/src/form.scss b/ui-ngx/src/form.scss index 4a4d018f26..14a3455685 100644 --- a/ui-ngx/src/form.scss +++ b/ui-ngx/src/form.scss @@ -54,6 +54,16 @@ padding: 12px; gap: 8px; } + &.no-padding-right { + padding-right: 0; + .mat-expansion-panel { + &.tb-settings { + > .mat-expansion-panel-header { + padding-right: 16px; + } + } + } + } &.no-padding-bottom { padding-bottom: 0; } From 1584d8db671999cb9dd8c21fce3e78bb10e62420 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 20 Dec 2024 11:37:19 +0200 Subject: [PATCH 021/108] UI: Update bundle image --- .../system/widget_bundles/high_performance_scada_oil_gas.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/data/json/system/widget_bundles/high_performance_scada_oil_gas.json b/application/src/main/data/json/system/widget_bundles/high_performance_scada_oil_gas.json index a2f798c4a4..87b778be21 100644 --- a/application/src/main/data/json/system/widget_bundles/high_performance_scada_oil_gas.json +++ b/application/src/main/data/json/system/widget_bundles/high_performance_scada_oil_gas.json @@ -2,7 +2,7 @@ "widgetsBundle": { "alias": "high_performance_scada_oil_gas", "title": "High-performance SCADA oil & gas", - "image": "tb-image:aHBfc2NhZGFfb2lsX2dhc19zeXN0ZW1fYnVuZGxlX2ltYWdlLnBuZw==:IkhpZ2gtcGVyZm9ybWFuY2UgU0NBREEgb2lsICYgZ2FzIiBzeXN0ZW0gYnVuZGxlIGltYWdl:SU1BR0U=;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAb1BMVEXe3t7b29vf398AAADf39/e3t7e3t7////ExMTGxsbj4+O5ubnQ0NDU1NStra3S0tKhoaHx8fGVlZWpqanV1dWysrK+vr6mpqa2trbLy8vBwcHa2trX19e7u7uampqKioqvr6+jo6Oenp77+/t6enr5/tYxAAAABnRSTlPvIK8Av79l/pT7AAAI50lEQVR42uza3W6jMBCG4f3T5zFjbCeIQKqkTVZ7/9e4yGEzAopYYyqgu+9BoO1JH40LxM2X719/fMHe+/b1+5ev+2c0NYxv+BR9gmXVjuSzQPAfsrX+bQhTycRMhO00A+IYoEs4ZWymeIh1aCKECOn9Knr9wnTpEEfXC4AzHjGSq1SvCpOlQwwBlwUk13vBt7LWTVkfkukmOp2KuvSQloU4IEgE4jCnF/VXvUBaFkLh9XyBQZvBsx1B+I9H3ylpbVX5YZqR5UdIi0IIj0qAXJjNucSsjLVVXXD2PiFnrkrPBEgLQ+TIdDbn1OuWfh+iEVHC0jIecAG1Vwih6UJwJbuLSYAYtqExiH1kIC0KATeOcwsypllb5xMiI1vaMvytT3bQp2vt6SMgDpdr55JLhJiKW52pyHK6vS4PMRd0IYhKq1npxSH46ToQj31CPJF1EIdleuMdQhgGsDjjkdEG3uwRQo/Xk2XAsGE0DmhEVBUnyuMQGd3rcvGJtCDDTBrBAUZkfCtudZ6Fxq67WSivr8WdIC04ETnqhwOEORkKjd0QKWQgLQoxXUhwwI1KNvuI4rgD8WbiaWuzEO4ssaPMZ07EvmLOR/7AuaitJUBaEkLPEwN4Fl98nrODmuiQ6w96z65FRN7oUcjm3+qyQDxBWGjbIcQb0BPiZkh8WVxL0lnT4d2bSF6/2Lq6QloO4hza3gwA2wfGZ6hpuEE3cQtJhjDaNIdXkrWV0pvu9YaIUiAaJIeOMKnjETElQPTzQM9jyCKm9SH85FDnBBoxbQSiIZCnhBDR6hByADQEIqeMiFaHGAAafQhodxAGNIYQ0N6Wlu09lMhXO5sIafQg8hOHv291iEcPMnO7cW0I0zjEWkS0MoRAYxBLjIjWnghAXYg4sKulNdgOEgf2NBFqXwQiDkRdtjYwEYAgEHFEjWRlCA8Go1vHziAkJwIJjtgn+Y1MBCCB8Jx3u5uBgP5A2GB/EAOJHhBxRG0JrQtxDEgWTd50JxbRihAGeiPh7rf2MpH+b01sNPY4Ed2DMGGfEEaos/f7CZaW7P0mXLZWgTgHSF72fpdaW8eqen2tqkhLPIQ7DiN7vwutLZerUE6IKBHijez9LgWxBxU6MCZKhOiOo7/3m76TfVNtd0yVPhFxCAQEsSZUR3wKOxEiDoF0JQ7z+6naPnhpkRNHDwIS7PxOqq3ERGkQI44BBOzSIXXEv3FTICyOIQT+IVnkIynaYaIkiBVHHyISxuw4kw9nYaIkCImjAxFJ2paQV888JkqCsDgGEJEQZlbdqKipqSjoVmFGAolwCKQnYcyLMqUKUk11oVR2xUjpEBLHACKS2ZBKKVZFgCieuCcmTkQcQ4hICLM6al1kSj8gWaH1K0ZKh/xu706U04aBMAD3XO1hWQhwCLRNerz/O1YRcly8GFFb6rid/i0Yk5l2v6xsK4IJg0NDBkkLsyLmRYEmBOPDTGcXQJ6DIw+BrYMZORyMIb+LHaEnH7ZdBzplIO4uCAr8fg6eAoBYTIgw0QtLHyZFIALiMpDZL+52bCLEeAkbNvKyZ5SkDAQhSLIQnDWTP5kICYr4N0H03LFQRwbJJARnXRGdPUPCzbIPO48RomYqpSCXkhYFQwTbC8ccCHGs+ylsOCTc7ypCGriUtOoB4MwlIRcbEK+G5D2ZYAqP1CS4BGQoT3AKsuCtdA/GWCI0ESMm7lk1dSwDkWFI9VtE+XVoLXi5R6w5hzkgODOZLwEZJKk7CKr8WedfF0u3NNwb14FKAQjBWNJQA81mcCyBAH4/nbrdjmMnnuMOgk7BjgySYHBpWBVbpOu+WX7IzuBLQAZJe15WaaIFi61kNwj5LIA4ByOJnM/I6Ej1QKBIKkB0bS1KutgDqrGEUCr1IdBuAUBEHMio7nVDCEYZzsIb/SUolvodgb4TDlQIyqU+BHG7lbbdIoKKgyKpABFd2gZTtqAiUCBVIA2otOpBnzVDVGW3IQjFUh+C2CLGOxhlzR3ByScJQYcclEr9jgCeIa2WlBlb5SETjtQRaFc0tnIQvPpUgmjJeiGqrtGbM1cz2/pNyOTbZVPW2xG8tj9AtMQ5uDvNQyP7Pe1E9iq0l/MtfU3KdgRhBNESuVdxOg4vgVrL5jJs6HwjE0OLINqhIEoi9zEe3ahw4poQHO0piJbQPY4jGB2qCGlhSLMBDdEShHwObK7Ecz2IaIeGJMn9EGcmwtUgzcihIFrishK86MLFYW4rQRwqh4bENM3dR3vnX8uOCm8tDZI6EFEOBdESzPTjR18qeWMpKKw31FPYVoMMDg3RknxHZFh+Z+vTCja/Huh1IKQcCqIkGQhyP6zYmtd4MtwLqnREORRES/CehnifKkwJe5SEVSDKoSBa4m5JXKpzZ8RchJltIpaHOKccGqIlAtN5SGUZq64hvi+5PES0Q0O0xOWvIdbra7tNvbAVINqhIVqCMJ1j/+uNjIYYW6sjpB0aoiUt3MhQs+5I6oUv3xHt0BAtIZgOJQhpiGebtsUhbXDkIVqCOQiHPzo2PVkcIluYA4FGchA09k92pIF5ELgBadL33E4c7FXOWjIXgjCZz6noKxBcH0Ty1xGjJcRcCYJzIeRgKl8SxNPYISY9xevpCGD2x1wyI4n3PglO8JITdNCE20kWQwRykAVjy7OXkSPtf1Gzs6UQrAJxvq88XjdSLPvHNLCOxSECNYYWpILPbyu1aeY1/HSyh78F4hqTwi+GEDaeuOchrGlo3VzJposlX7IULKZ3dLCqjoDAjXRspvII5SGiqh/Sprs+zW9B4GlC4h1UgKjijjyZI4yS+W8fflxzcAc1IC2M8slM5lOmIyp4ujKsCKpAZAkEIdMSJjIXsdZ3dSDNEojD/LIpU1pojCfhuKkBcW4WJD+2OhE54DlPz7uQ513aPYjIsTREYBEEMytb0//UyiDTHWnpZr7CKBuKeZoLoeHhl7TlyViI2StIwRToiG5FvjEIM1IFssHXHPnOnPA18gck/z/YcdX5D1lb/pkP1n3zAf6JvPt3PkT7/du//2O033x8+/4nILYzAoIqAxwAAAAASUVORK5CYII=", + "image": "tb-image:aHBfc2NhZGFfb2lsX2dhc19zeXN0ZW1fYnVuZGxlX2ltYWdlLnBuZw==:IkhpZ2gtcGVyZm9ybWFuY2UgU0NBREEgb2lsICYgZ2FzIiBzeXN0ZW0gYnVuZGxlIGltYWdl:SU1BR0U=;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAflBMVEXe3t7b29vf398AAADf39/e3t7e3t7////Gxsapqam5ubnR0NDj4+Otra2hoaHS0tLi4uK4uLjx8fGYmJikpKSdnZ2VlZWWlpbU1NS1tbWnp6eKioqysrLLy8uampra2tqvr6/X19eRkZF+fn7Dw8N1dXW/vr6FhYViYmJwcHCzXr36AAAABnRSTlPvIK8Av79l/pT7AAAI0UlEQVR42uza23LaMBSF4Z5msbyFpOFMAXcg0HT6/i/YGqvdNhBiYXtipf0v7CRX+UaSbWQ+fP745QNS79PHzx8+ps8A8JvxCe+idzCtwpC8Fwj+Q4bWvw0R7oQiJIbTAxAvwOmAIsFgiodYXxxPODecMYmGeD4fAOQoGtCYxEIMgcMQJbEQD5wlCvEYRLEQoig/wCBkMIgiIYKyE+claTBzKxJClO0AegDI8x0GUTREz5NTbvJUR0RwzjjAn1GpQlgcDif4nfiDAZAqBAIc8nBjNyY3ef6MQRR/HzlI7ZI7lCfHaIg5oA5Bu5bZzZaIKh6Cha9BHFq2HN2sb4gjra84rHAreCDKcjUr+n4b8n1WtHKW0LqDSGGwyFFmaOBMLGRv57uM4+moSWNONvuF7RrC8vhsBTBSEJwBEdfXUXRfux+RADIiJM4OSIIQVs8sHUmOiKlBSgd8pGS/2M8zrhutkemY2XyZ7TuGeKlBXHAJ4qN1q9mP+4ofs5WTXq5agipkqePzYLP7kBkiioIQIRrAifouSggCOsN0IaIQxyorJiOrb4uy5StrZFG2WVnTF8QZkAh5NjW4ebbKxqPo1pPdk2paQ7xHaGsAWMTMreXW7ibTUYum3DxtXBcQHRA5H6PmVjbqpKxLCEE9FUliEP49Uc9FNjGIBIdCwHSnFm8cwKQg9PqPXxgEr3ZkB4zp+tgBxNSWif7cDAJx3542nLa4+NrtkugAItefR3TxN4x2tthN1uPQ6G7jP01W2dyxszu7BXELAkIQk7Gh433I0ZaJ6fZZi8RtCEiPagN/aHS4hNSN2rAhwpch1qLasCEEX4JYCuIy4lbfXn+M36ycGHQMEby02K0aG27QbSbjadM7Bzfzhe0YAtYg6oAASGU7iLi8IaoD9OlAJBAUog5AkoHI1cBQHVEQu3nabyacNlsi2W6ebdkhRBe6QtQBIjKxDV4rzB0NtE4goiSFSI131SBf9ChEP+qKwV3IIF+9GWgsIRUHPNFDPUC8oJItDq7iePvX7U0hgmokIALt7b9L9yAEFENo6YwIL34TIk2IQPu796ulOrXC3q/29pethhDvoYV3PBzU3GoIEVRypr73W/TmcysaUjiqe7+hVCBE6OwIkPrfLTqvzxEpHApRSSojEiDBcXv/2qPv2kPoURQcNYhKBH3XHmJQFhw1COSsTAMixUEddQhcKSH6rj3EAlDHBaSUpDEiBCqOS0iQ0KPbeppa6riGBAn6rD3kynENKSWCfmsP4YXjGlJIHoYcs4t+ekQUARF1XENUQjyUrEcXrYmIYiDquIao5Fd797rcJgxEAbjX1UogBBRfErupm16mff8XbO2RWYuDqyCBx+30/EiMmTb6ugtSgYwtJcW2Q0hbUSSJkJ04ECJZO0rJRkE2hJkF4l4EYUMp0Qh5IswMEEPGvQDClAZ5RoglzAwQJpFch3DqRLK/WWsZEslVCFMqRE+4H5ILCSWWDf+OYQu0hDhGCDNh8iEFhRIbvhCCS5HYEiG1IUguRIZn+Cok55crqwm3R/IgRlrq/J3ZXLZW1u2exwnPCmRDROJj4PGB5Io8T3i+LA/CNJQUXFBRyf4syGEM0hFkvoqIpCJyFhxzttaeILNBRGKJ2BEVFsbupktcMwZpHEFyIc7RQGL8Y4GOxZHUW7KIjy3k8yE4NsvGT/bE4kjuraoFBS7kF4GQXR/fNcaRYcqGbJSKH+05EBwbnoUr3EVToxUGF/JLVETmePcCdco0ggv5hSDM67Wxds1MEEcTs79VaxkcWsU+6yg7sbVwIZ8PKQhi5UU2hFmpCeffnIpMg0z9+bYch9SWJEtBmC3z6UtuRar1D30lP9YVQXIgfPXNEcfEK9mdL0ezC868jS+KSJaoiBDYMmX1Fu/9iJvgiNeN9z06yoLEHb4iZPN6a3UugWpacbSstN98pvkgPPqWh4gkBdKVZ4falgKpWbXavzzQghXBhzNRPqWxjo1UbwXCx+1GmmshCD4uC39gSmP5MdccQI46aa5lWotDDUqcm9pYYxClW2muBSoy/kh5Ukm+6VO++Hm8d/jjpf3p9ydD4g6BiGQ6pPPnWqmCHOyyQ3UzQRi3QghKOAHSBPPIEhBLkqIigIhkIoR3/ajbAeTpEvKFZ28tcQhEJNMghWbuIY0uw5m91rqHMAslB1KEDoSgxMV/sOmUuoAoHa61tuoColTH+RDH6BBIIBayiToeVAhRtRaHLlUIUQ8mG2LAARCQRHuLH9UAIlO5nwp1K5CjhLMh4AAISKIV2akAwuW5DnLyrTmAqEMuhMEBEJDEIPsygPgRw7cA0la5FQEHQFDCsStyoxBuL9cqTQhRXT5EHAhBSfy0ZcsBpJHp4wgYh9Q2C+IcOBCCEhO/iyCQmi8ndN2vHEOI+pAFMehACEpc/MqiQJp2DNI2A4jNg6ADISjh+JVFgWjlcxKx8tEDSJcFYXQgBCU2slgEiPzPkHvWrBCDDoSghJMgzWnzGuSQBbEVRSDTJXoA2fbNFKzn6wFklQMxa0qBUGGi04hAlEBgc67WKigNQuYFV60bfcpOha3V9mtgfUrjPZxVkVQIT3xkQ2b2VqvRVHRzSHRGtFuFkUVWG7k2nwLhFEj8SvbTHxyqHd27o2UrktZbrhtxlP0/fqMgmyILYigJEl/JM0iai3bbNuBgyoLwUhByq1JdpNXBZv3UBpvPUo+bV4QpkvWu7BkffsAh8bWntN2B6I4hRHa1b5ibx9WmYK/qx87UrR6ZWW+0JclNWivpSjaz75oPD+L4+sFPxzIJ3q4i+Xepv5yXJKuCIFkQHAtLrP9yTpEKkXBX6YfDLsJIgcBf+bG8mo9YstTMD4HD7JO6mk80iKGFkt9aUyD3VRForSkQlyiZHYJn0EmQxN5aAIIjmQi5n4oMIX9tRUaer66vZkunfCafe4LISKAU8cLcU2tV3Odj+cJ85z7mBpL/H+x41/kPubf8Mx+s++od/RN58+98iPbb13//x2i/ev/67S8YXTU9MiU8PgAAAABJRU5ErkJggg==", "scada": true, "description": "Bundle with high-performance SCADA symbols for oil and gas system", "order": 9405, From 5840bf1f671bca925d42b4ccff06e06e9be5aa9c Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 20 Dec 2024 18:08:53 +0200 Subject: [PATCH 022/108] UI: Import/Export support for dynamic form properties. Improve json schema form to dynamic form conversion. --- .../dynamic-form-properties.component.html | 31 +++++++++- .../dynamic-form-properties.component.ts | 58 ++++++++++++++++-- ...dynamic-form-property-panel.component.html | 16 +++++ .../dynamic-form-property-panel.component.ts | 14 ++++- .../dynamic-form/dynamic-form.component.html | 2 +- .../dynamic-form/dynamic-form.component.ts | 10 +++ .../scada-symbol-metadata.component.html | 2 + .../scada-symbol-metadata.component.ts | 2 +- .../pages/widget/widget-editor.component.html | 4 +- .../import-export/import-export.service.ts | 29 +++++++++ .../app/shared/models/dynamic-form.models.ts | 61 ++++++++++++++----- .../assets/locale/locale.constant-en_US.json | 11 +++- ui-ngx/src/form.scss | 2 +- 13 files changed, 214 insertions(+), 28 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.html index cfdcfacfc4..5a37c43486 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.html @@ -21,7 +21,36 @@
dynamic-form.property.id
dynamic-form.property.name
dynamic-form.property.type
-
+
+ + + +
{ - let properties: FormProperty[] = this.propertiesFormGroup.get('properties').value; - if (properties) { - properties = properties.filter(p => propertyValid(p)); - } + const properties = this.getProperties(); this.booleanPropertyIds = properties.filter(p => p.type === FormPropertyType.switch).map(p => p.id); properties.forEach((p, i) => { if (p.disableOnProperty && !this.booleanPropertyIds.includes(p.disableOnProperty)) { @@ -222,6 +230,38 @@ export class DynamicFormPropertiesComponent implements ControlValueAccessor, OnI }); } + export($event: Event) { + if ($event) { + $event.stopPropagation(); + } + const properties = this.getProperties(); + this.importExportService.exportFormProperties(properties, this.exportFileName); + } + + import($event: Event) { + if ($event) { + $event.stopPropagation(); + } + this.importExportService.importFormProperties().subscribe((properties) => { + if (properties) { + this.propertiesFormGroup.setControl('properties', this.preparePropertiesFormArray(properties), {emitEvent: true}); + } + }); + } + + clear($event: Event) { + if ($event) { + $event.stopPropagation(); + } + this.dialogService.confirm(this.translate.instant('dynamic-form.clear-form'), + this.translate.instant('dynamic-form.clear-form-prompt'), null, this.translate.instant('action.clear')) + .subscribe((clear) => { + if (clear) { + (this.propertiesFormGroup.get('properties') as UntypedFormArray).clear({emitEvent: true}); + } + }); + } + private preparePropertiesFormArray(properties: FormProperty[] | undefined): UntypedFormArray { const propertiesControls: Array = []; if (properties) { @@ -231,4 +271,12 @@ export class DynamicFormPropertiesComponent implements ControlValueAccessor, OnI } return this.fb.array(propertiesControls); } + + private getProperties(): FormProperty[] { + let properties: FormProperty[] = this.propertiesFormGroup.get('properties').value; + if (properties) { + properties = properties.filter(p => propertyValid(p)); + } + return properties; + } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.html index c10aaaf803..5e8f9b9928 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.html @@ -267,6 +267,22 @@ {{ 'dynamic-form.property.allow-empty-select-option' | translate }}
+
+
dynamic-form.property.selected-options-limit
+
+
dynamic-form.property.min
+ + + + +
dynamic-form.property.max
+ + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.ts index ee19d25a3d..960efa55fc 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.ts @@ -124,6 +124,8 @@ export class DynamicFormPropertyPanelComponent implements OnInit { properties: [this.property.properties, []], multiple: [this.property.multiple, []], allowEmptyOption: [this.property.allowEmptyOption, []], + minItems: [this.property.minItems, []], + maxItems: [this.property.maxItems, []], items: [this.property.items, []], helpId: [this.property.helpId, []], direction: [this.property.direction || 'column', []], @@ -208,13 +210,19 @@ export class DynamicFormPropertyPanelComponent implements OnInit { const multiple: boolean = this.propertyFormGroup.get('multiple').value; if (multiple) { this.propertyFormGroup.get('allowEmptyOption').disable({emitEvent: false}); + this.propertyFormGroup.get('minItems').enable({emitEvent: false}); + this.propertyFormGroup.get('maxItems').enable({emitEvent: false}); } else { this.propertyFormGroup.get('allowEmptyOption').enable({emitEvent: false}); + this.propertyFormGroup.get('minItems').disable({emitEvent: false}); + this.propertyFormGroup.get('maxItems').disable({emitEvent: false}); } } } else { this.propertyFormGroup.get('multiple').disable({emitEvent: false}); this.propertyFormGroup.get('allowEmptyOption').disable({emitEvent: false}); + this.propertyFormGroup.get('minItems').disable({emitEvent: false}); + this.propertyFormGroup.get('maxItems').disable({emitEvent: false}); this.propertyFormGroup.get('items').disable({emitEvent: false}); } if (type === FormPropertyType.datetime) { @@ -290,7 +298,9 @@ export class DynamicFormPropertyPanelComponent implements OnInit { this.propertyFormGroup.get('default').patchValue(newVal, {emitEvent: false}); } this.propertyFormGroup.get('allowEmptyOption').patchValue(false, {emitEvent: false}); - this.propertyFormGroup.get('allowEmptyOption').disable({emitEvent: false}); + this.propertyFormGroup.get('allowEmptyOption').disable({emitEvent: false}) + this.propertyFormGroup.get('minItems').enable({emitEvent: false}); + this.propertyFormGroup.get('maxItems').enable({emitEvent: false}); } else { if (defaultValue && Array.isArray(defaultValue)) { const newVal = defaultValue.length ? defaultValue[0] : null; @@ -299,6 +309,8 @@ export class DynamicFormPropertyPanelComponent implements OnInit { }); } this.propertyFormGroup.get('allowEmptyOption').enable({emitEvent: false}); + this.propertyFormGroup.get('minItems').disable({emitEvent: false}); + this.propertyFormGroup.get('maxItems').disable({emitEvent: false}); } } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.html index c617ef23d5..e598fd02db 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.html @@ -88,7 +88,7 @@ -
+
{{ propertyRow.label | customTranslate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.ts index 3c9d7a1f83..432bacf4f1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form.component.ts @@ -215,6 +215,16 @@ export class DynamicFormComponent implements OnInit, OnChanges, ControlValueAcce validators.push(Validators.max(property.max)); } } + if (property.type === FormPropertyType.select) { + if (property.multiple) { + if (isDefinedAndNotNull(property.minItems)) { + validators.push(Validators.minLength(property.minItems)); + } + if (isDefinedAndNotNull(property.maxItems)) { + validators.push(Validators.maxLength(property.maxItems)); + } + } + } this.propertiesFormGroup.addControl(property.id, this.fb.control(null, validators), {emitEvent: false}); } } diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.html b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.html index 00a18cc2ab..f90a5bef64 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.html +++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.html @@ -107,6 +107,8 @@
diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.ts b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.ts index a5d38991b9..5846f98d0c 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.ts +++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.ts @@ -90,7 +90,7 @@ export class ScadaSymbolMetadataComponent extends PageComponent implements OnIni @Input() tags: string[]; - private modelValue: ScadaSymbolMetadata; + modelValue: ScadaSymbolMetadata; private propagateChange = null; diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html index 6f77256e9e..02f0ecead8 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html @@ -188,9 +188,11 @@
- +
diff --git a/ui-ngx/src/app/shared/import-export/import-export.service.ts b/ui-ngx/src/app/shared/import-export/import-export.service.ts index 5b707d6399..7620249034 100644 --- a/ui-ngx/src/app/shared/import-export/import-export.service.ts +++ b/ui-ngx/src/app/shared/import-export/import-export.service.ts @@ -87,6 +87,7 @@ import { ExportResourceDialogData, ExportResourceDialogDialogResult } from '@shared/import-export/export-resource-dialog.component'; +import { FormProperty, propertyValid } from '@shared/models/dynamic-form.models'; export type editMissingAliasesFunction = (widgets: Array, isSingleWidget: boolean, customTitle: string, missingEntityAliases: EntityAliases) => Observable; @@ -119,6 +120,26 @@ export class ImportExportService { } + public exportFormProperties(properties: FormProperty[], fileName: string) { + this.exportToPc(properties, fileName); + } + + public importFormProperties(): Observable { + return this.openImportDialog('dynamic-form.import-form', 'dynamic-form.form-json-file').pipe( + map((properties: FormProperty[]) => { + if (!this.validateImportedFormProperties(properties)) { + this.store.dispatch(new ActionNotificationShow( + {message: this.translate.instant('dynamic-form.invalid-form-json-file-error'), + type: 'error'})); + throw new Error('Invalid form JSON file'); + } else { + return properties; + } + }), + catchError(() => of(null)) + ); + } + public exportImage(type: ImageResourceType, key: string) { this.imageService.exportImage(type, key).subscribe( { @@ -927,6 +948,14 @@ export class ImportExportService { type: 'error'})); } + private validateImportedFormProperties(properties: FormProperty[]): boolean { + if (!properties.length) { + return false; + } else { + return !properties.some(p => !propertyValid(p)); + } + } + private validateImportedImage(image: ImageExportData): boolean { return !(!isNotEmptyStr(image.data) || !isNotEmptyStr(image.title) diff --git a/ui-ngx/src/app/shared/models/dynamic-form.models.ts b/ui-ngx/src/app/shared/models/dynamic-form.models.ts index 773f6bdebe..14df1ff3b2 100644 --- a/ui-ngx/src/app/shared/models/dynamic-form.models.ts +++ b/ui-ngx/src/app/shared/models/dynamic-form.models.ts @@ -132,6 +132,8 @@ export interface FormSelectProperty extends FormPropertyBase { multiple?: boolean; allowEmptyOption?: boolean; items?: FormSelectItem[]; + minItems?: number; + maxItems?: number; } export type FormPropertyDirection = 'row' | 'column'; @@ -311,6 +313,11 @@ const toPropertyContainers = (properties: FormProperty[], visible: true }; result.push(propertyRow); + const rowClasses = (propertyRow.rowClass || '').split(' ').filter(cls => cls.trim().length > 0); + if (!rowClasses.includes('flex-wrap')) { + rowClasses.push('flex-wrap'); + } + propertyRow.rowClass = rowClasses.join(' '); } if (property.type === FormPropertyType.switch) { propertyRow.switch = property; @@ -330,10 +337,18 @@ const toPropertyContainers = (properties: FormProperty[], delete container.properties; delete container.rowClass; } else { - if (!container.rowClass) { - container.rowClass = 'column-xs'; - container.propertiesRowClass = 'gt-xs:align-center xs:flex-col gt-xs:flex-row gt-xs:justify-end'; + container.propertiesRowClass = 'gt-xs:align-center xs:flex-col gt-xs:flex-row gt-xs:justify-end'; + const rowClasses = (container.rowClass || '').split(' ').filter(cls => cls.trim().length > 0); + if (!rowClasses.includes('column-xs')) { + rowClasses.push('column-xs'); + } + if (property.fieldClass && property.fieldClass.split(' ').includes('flex')) { + container.propertiesRowClass += ' overflow-hidden'; + if (rowClasses.includes('flex-wrap')) { + rowClasses.splice(rowClasses.indexOf('flex-wrap'), 1); + } } + container.rowClass = rowClasses.join(' '); } } } @@ -600,7 +615,7 @@ const jsonFormDataToProperty = (form: JsonFormData, level: number, groupTitle?: name: form.title || id, group: groupTitle, type: null, - default: form.schema?.default || null, + default: (isDefinedAndNotNull(form.default) ? form.default : form.schema?.default) || null, required: isDefinedAndNotNull(form.required) ? form.required : false }; if (form.condition?.length) { @@ -635,6 +650,14 @@ const jsonFormDataToProperty = (form: JsonFormData, level: number, groupTitle?: property.multiple = form.multiple; property.fieldClass = 'flex'; property.allowEmptyOption = isDefinedAndNotNull(form.allowClear) ? form.allowClear : false; + if (property.multiple) { + if (typeof (form.schema as any)?.minItems === 'number') { + property.minItems = (form.schema as any).minItems; + } + if (typeof (form.schema as any)?.maxItems === 'number') { + property.maxItems = (form.schema as any).maxItems; + } + } break; case 'select': property.type = FormPropertyType.select; @@ -699,17 +722,25 @@ const jsonFormDataToProperty = (form: JsonFormData, level: number, groupTitle?: break; case 'array': if (form.items?.length) { - const item: JsonFormData = form.items[0] as JsonFormData; - if (item.type !== 'array') { - const arrayProperty = jsonFormDataToProperty(item, 0); - arrayProperty.arrayItemType = arrayProperty.type; - arrayProperty.arrayItemName = arrayProperty.name; - arrayProperty.id = property.id; - arrayProperty.name = property.name; - arrayProperty.group = property.group; - arrayProperty.condition = property.condition; - arrayProperty.required = property.required; - property = arrayProperty; + const arrayItemSchema = form.schema.items; + if (arrayItemSchema && arrayItemSchema.type && arrayItemSchema.type !== 'array') { + if (arrayItemSchema.type === 'object') { + property.arrayItemType = FormPropertyType.fieldset; + property.arrayItemName = ''; + property.properties = form.items ? (form.items as JsonFormData[]).map(item => + jsonFormDataToProperty(item, level+2)).filter(p => p !== null) : []; + } else { + const item: JsonFormData = form.items[0] as JsonFormData; + const arrayProperty = jsonFormDataToProperty(item, 0); + arrayProperty.arrayItemType = arrayProperty.type; + arrayProperty.arrayItemName = arrayProperty.name; + arrayProperty.id = property.id; + arrayProperty.name = property.name; + arrayProperty.group = property.group; + arrayProperty.condition = property.condition; + arrayProperty.required = property.required; + property = arrayProperty; + } property.type = FormPropertyType.array; } } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 58e947e80e..a8ed773047 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1657,6 +1657,7 @@ "min": "Min", "max": "Max", "step": "Step", + "selected-options-limit": "Selected options limit", "advanced-ui-settings": "Advanced UI settings", "disable-on-property": "Disable on property", "display-condition-function": "Display condition function", @@ -1693,7 +1694,13 @@ "item-type": "Item type", "item-name": "Item name", "no-items": "No items" - } + }, + "clear-form": "Clear form", + "clear-form-prompt": "Are you sure you want to remove all form properties?", + "import-form": "Import form from JSON", + "export-form": "Export form to JSON", + "form-json-file": "Form JSON file", + "invalid-form-json-file-error": "Unable to import form from JSON: Invalid form JSON data structure." }, "asset-profile": { "asset-profile": "Asset profile", @@ -5473,7 +5480,7 @@ "html": "HTML", "tidy": "Tidy", "css": "CSS", - "settings-form-properties": "Settings form properties", + "settings-form": "Settings form", "settings-schema": "Settings schema", "datakey-settings-schema": "Data key settings schema", "latest-datakey-settings-schema": "Latest data key settings schema", diff --git a/ui-ngx/src/form.scss b/ui-ngx/src/form.scss index 14a3455685..0ae2b9584b 100644 --- a/ui-ngx/src/form.scss +++ b/ui-ngx/src/form.scss @@ -223,7 +223,7 @@ } .mat-divider-vertical { height: 56px; - margin-top: -7px; + margin-top: -9px; margin-bottom: -7px; } .mat-mdc-form-field, tb-unit-input { From 1d4cd01e84292dbe3b4e8fe958db9964f7a2a199 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 23 Dec 2024 11:18:20 +0200 Subject: [PATCH 023/108] Fix MqttTransportHandler compile error --- .../server/transport/mqtt/MqttTransportHandler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index 2b7e2a51dc..e669016514 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -94,6 +94,7 @@ import org.thingsboard.server.transport.mqtt.util.sparkplug.SparkplugTopic; import javax.net.ssl.SSLPeerUnverifiedException; import java.io.IOException; import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.ArrayList; @@ -109,7 +110,6 @@ import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; -import static com.amazonaws.util.StringUtils.UTF8; import static io.netty.handler.codec.mqtt.MqttMessageType.CONNECT; import static io.netty.handler.codec.mqtt.MqttMessageType.PINGRESP; import static io.netty.handler.codec.mqtt.MqttMessageType.SUBACK; @@ -606,7 +606,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } private void getOtaPackageCallback(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, int msgId, Matcher fwMatcher, OtaPackageType type) { - String payload = mqttMsg.content().toString(UTF8); + String payload = mqttMsg.content().toString(StandardCharsets.UTF_8); int chunkSize = StringUtils.isNotEmpty(payload) ? Integer.parseInt(payload) : 0; String requestId = fwMatcher.group("requestId"); int chunk = Integer.parseInt(fwMatcher.group("chunk")); From e561537048d37f3b570ae6cf6a6617286a02adc9 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 23 Dec 2024 11:23:56 +0200 Subject: [PATCH 024/108] UI: Migrating ESLint config to flat format --- ui-ngx/.eslintrc.json | 74 ----------- ui-ngx/eslint.config.mjs | 81 ++++++++++++ ui-ngx/package.json | 15 +-- ui-ngx/yarn.lock | 259 ++++++++++++++++++++++----------------- 4 files changed, 230 insertions(+), 199 deletions(-) delete mode 100644 ui-ngx/.eslintrc.json create mode 100644 ui-ngx/eslint.config.mjs diff --git a/ui-ngx/.eslintrc.json b/ui-ngx/.eslintrc.json deleted file mode 100644 index 2d17064e40..0000000000 --- a/ui-ngx/.eslintrc.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "root": true, - "ignorePatterns": [ - "projects/**/*" - ], - "overrides": [ - { - "files": [ - "*.ts", - "*.tsx" - ], - "parserOptions": { - "project": [ - "tsconfig.json" - ], - "createDefaultProgram": true - }, - "extends": [ - "plugin:@angular-eslint/recommended", - "plugin:@angular-eslint/template/process-inline-templates" - ], - "rules": { - "@typescript-eslint/explicit-member-accessibility": [ - "off", - { - "accessibility": "explicit" - } - ], - "arrow-parens": [ - "off", - "always" - ], - "@angular-eslint/component-selector": [ - "error", - { - "prefix": [ - "tb" - ] - } - ], - "id-blacklist": [ - "error", - "any", - "Number", - "String", - "string", - "Boolean", - "boolean", - "Undefined", - "undefined" - ], - "import/order": "off", - "@typescript-eslint/member-ordering": "off", - "no-underscore-dangle": "off", - "@typescript-eslint/naming-convention": "off", - "jsdoc/newline-after-description": 0 - } - }, - { - "files": [ - "*.html" - ], - "extends": [ - "plugin:@angular-eslint/template/recommended", - "plugin:tailwindcss/recommended" - ], - "rules": { - "tailwindcss/no-custom-classname": "off", - "tailwindcss/migration-from-tailwind-2": "off", - "tailwindcss/enforces-negative-arbitrary-values": "off" - } - } - ] -} diff --git a/ui-ngx/eslint.config.mjs b/ui-ngx/eslint.config.mjs new file mode 100644 index 0000000000..aaecc6d5ab --- /dev/null +++ b/ui-ngx/eslint.config.mjs @@ -0,0 +1,81 @@ +import eslintJS from "@eslint/js"; +import tsEslint from "typescript-eslint"; +import angular from "angular-eslint"; +import tailwind from "eslint-plugin-tailwindcss"; + +export default tsEslint.config( + { + files: ["**/*.ts", "*.tsx"], + languageOptions: { + parserOptions: { + project: true, + tsconfigRootDir: import.meta.dirname, // or import.meta.dirname for ESM + }, + }, + extends: [ + eslintJS.configs.recommended, + ...tsEslint.configs.recommended, + ...tsEslint.configs.stylistic, + ...angular.configs.tsRecommended, + ], + processor: angular.processInlineTemplates, + rules: { + "@typescript-eslint/explicit-member-accessibility": [ + "off", + { + accessibility: "explicit" + } + ], + "arrow-parens": [ + "off", + "always" + ], + "@angular-eslint/component-selector": [ + "error", + { + prefix: [ + "tb" + ] + } + ], + "id-blacklist": [ + "error", + "any", + "Number", + "String", + "string", + "Boolean", + "boolean", + "Undefined", + "undefined" + ], + "import/order": "off", + "@typescript-eslint/member-ordering": "off", + "no-underscore-dangle": "off", + "@typescript-eslint/naming-convention": "off", + "jsdoc/newline-after-description": 0, + "@typescript-eslint/consistent-indexed-object-style": "off", + "@typescript-eslint/array-type": "off", + "no-extra-boolean-cast": "off", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-inferrable-types": "off", + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/ban-ts-comment": "off", + "no-case-declarations": "off" + }, + }, + { + files: ["**/*.html"], + extends: [ + ...angular.configs.templateRecommended, + ...angular.configs.templateAccessibility, + ...tailwind.configs["flat/recommended"] + ], + rules: { + "tailwindcss/no-custom-classname": "off", + "tailwindcss/migration-from-tailwind-2": "off", + "tailwindcss/enforces-negative-arbitrary-values": "off" + } + } +); diff --git a/ui-ngx/package.json b/ui-ngx/package.json index d8870a3568..c92cd7cf19 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -114,11 +114,6 @@ "@angular-devkit/build-angular": "18.2.12", "@angular-devkit/core": "18.2.12", "@angular-devkit/schematics": "18.2.12", - "@angular-eslint/builder": "18.4.2", - "@angular-eslint/eslint-plugin": "18.4.2", - "@angular-eslint/eslint-plugin-template": "18.4.2", - "@angular-eslint/schematics": "18.4.2", - "@angular-eslint/template-parser": "18.4.2", "@angular/build": "18.2.12", "@angular/cli": "18.2.12", "@angular/compiler-cli": "18.2.13", @@ -142,13 +137,10 @@ "@types/systemjs": "6.15.1", "@types/tinycolor2": "^1.4.6", "@types/tooltipster": "^0.0.35", - "@typescript-eslint/eslint-plugin": "^8.16.0", - "@typescript-eslint/parser": "^8.16.0", - "@typescript-eslint/types": "^8.16.0", - "@typescript-eslint/utils": "^8.16.0", + "angular-eslint": "~18.4.3", "autoprefixer": "^10.4.20", "directory-tree": "^3.5.2", - "eslint": "~9.15.0", + "eslint": "~9.17.0", "eslint-plugin-import": "latest", "eslint-plugin-jsdoc": "^50.6.0", "eslint-plugin-prefer-arrow": "latest", @@ -159,7 +151,8 @@ "postinstall-prepare": "^2.0.0", "tailwindcss": "^3.4.15", "ts-node": "^10.9.2", - "typescript": "~5.5.4" + "typescript": "~5.5.4", + "typescript-eslint": "^8.18.1" }, "resolutions": { "@types/react": "18.3.10", diff --git a/ui-ngx/yarn.lock b/ui-ngx/yarn.lock index b7e5e21e55..a0589d8398 100644 --- a/ui-ngx/yarn.lock +++ b/ui-ngx/yarn.lock @@ -34,7 +34,7 @@ "@angular-devkit/build-angular" "^18.0.0" "@angular-devkit/core" "^18.0.0" -"@angular-devkit/architect@0.1802.12", "@angular-devkit/architect@>=0.1800.0 < 0.1900.0": +"@angular-devkit/architect@0.1802.12", "@angular-devkit/architect@>= 0.1800.0 < 0.1900.0", "@angular-devkit/architect@>=0.1800.0 < 0.1900.0": version "0.1802.12" resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1802.12.tgz#096f8e9cf71f8848c6f0172c03f3f1135509e133" integrity sha512-bepVb2/GtJppYKaeW8yTGE6egmoWZ7zagFDsmBdbF+BYp+HmeoPsclARcdryBPVq68zedyTRdvhWSUTbw1AYuw== @@ -120,7 +120,7 @@ "@angular-devkit/architect" "0.1802.12" rxjs "7.8.1" -"@angular-devkit/core@18.2.12", "@angular-devkit/core@^18.0.0": +"@angular-devkit/core@18.2.12", "@angular-devkit/core@>= 18.0.0 < 19.0.0", "@angular-devkit/core@^18.0.0": version "18.2.12" resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-18.2.12.tgz#fb514e9b3c9ea87ddaa1582d3947f1b094c9b387" integrity sha512-NtB6ypsaDyPE6/fqWOdfTmACs+yK5RqfH5tStEzWFeeDsIEDYKsJ06ypuRep7qTjYus5Rmttk0Ds+cFgz8JdUQ== @@ -132,7 +132,7 @@ rxjs "7.8.1" source-map "0.7.4" -"@angular-devkit/schematics@18.2.12": +"@angular-devkit/schematics@18.2.12", "@angular-devkit/schematics@>= 18.0.0 < 19.0.0": version "18.2.12" resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-18.2.12.tgz#15d1a8611bf9f18215435604672411b1929bf4d1" integrity sha512-mMea9txHbnCX5lXLHlo0RAgfhFHDio45/jMsREM2PA8UtVf2S8ltXz7ZwUrUyMQRv8vaSfn4ijDstF4hDMnRgQ== @@ -143,59 +143,64 @@ ora "5.4.1" rxjs "7.8.1" -"@angular-eslint/builder@18.4.2": - version "18.4.2" - resolved "https://registry.yarnpkg.com/@angular-eslint/builder/-/builder-18.4.2.tgz#28a4833919ede3db0a1d905fd903485588282115" - integrity sha512-eyI9sreaM9ukA24PCJoSqsjCYOiBf3TZ/Q1WY8PG0SwQWc03qJNqPl5K+/Ptmsc1RtoDCLCU6uaOBFPhb9lDxw== +"@angular-eslint/builder@18.4.3": + version "18.4.3" + resolved "https://registry.yarnpkg.com/@angular-eslint/builder/-/builder-18.4.3.tgz#800b8a68b464ddfc0d737b0ad38c7804b463d8e1" + integrity sha512-NzmrXlr7GFE+cjwipY/CxBscZXNqnuK0us1mO6Z2T6MeH6m+rRcdlY/rZyKoRniyNNvuzl6vpEsfMIMmnfebrA== + dependencies: + "@angular-devkit/architect" ">= 0.1800.0 < 0.1900.0" + "@angular-devkit/core" ">= 18.0.0 < 19.0.0" -"@angular-eslint/bundled-angular-compiler@18.4.2": - version "18.4.2" - resolved "https://registry.yarnpkg.com/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-18.4.2.tgz#3d9827d6aea627e77a93b223af1dc56a3ea5e258" - integrity sha512-K7pqmZI3Dl75zlLexyaM7bw4xdgk/3bhP1B6uqDKML9+vIIvccCR2bGvqFurqeFbJlMykzb3H4jytT+HpqV4tg== +"@angular-eslint/bundled-angular-compiler@18.4.3": + version "18.4.3" + resolved "https://registry.yarnpkg.com/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-18.4.3.tgz#0810f76045b854782e6370953cf5324112a65f80" + integrity sha512-zdrA8mR98X+U4YgHzUKmivRU+PxzwOL/j8G7eTOvBuq8GPzsP+hvak+tyxlgeGm9HsvpFj9ERHLtJ0xDUPs8fg== -"@angular-eslint/eslint-plugin-template@18.4.2": - version "18.4.2" - resolved "https://registry.yarnpkg.com/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-18.4.2.tgz#5cc70866b7d15fcc85f11cf497283969d3a98c9b" - integrity sha512-v9msmIdZK6lOEC4ScDeYKFLpszpJ5Ei+8ifkT7fXXKmPaWtPJtMbW+VGOUNm5Ezi+xByAGCn1qU+OF2aJ/4CLw== +"@angular-eslint/eslint-plugin-template@18.4.3": + version "18.4.3" + resolved "https://registry.yarnpkg.com/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-18.4.3.tgz#3e9820735f087afad193361e3081fad54dbf4e51" + integrity sha512-ijGlX2N01ayMXTpeQivOA31AszO8OEbu9ZQUCxnu9AyMMhxyi2q50bujRChAvN9YXQfdQtbxuajxV6+aiWb5BQ== dependencies: - "@angular-eslint/bundled-angular-compiler" "18.4.2" - "@angular-eslint/utils" "18.4.2" + "@angular-eslint/bundled-angular-compiler" "18.4.3" + "@angular-eslint/utils" "18.4.3" aria-query "5.3.2" axobject-query "4.1.0" -"@angular-eslint/eslint-plugin@18.4.2": - version "18.4.2" - resolved "https://registry.yarnpkg.com/@angular-eslint/eslint-plugin/-/eslint-plugin-18.4.2.tgz#c64b016f404521175c9484145f814787c2a31c00" - integrity sha512-Oem4W2P54cPADN9rJenLj90rqDPUQWx5kZiz84FCnsSn5DBdsI5LGQoogNT9y3Jx/9VL/VGIMMA5B6qG+0hVlg== +"@angular-eslint/eslint-plugin@18.4.3": + version "18.4.3" + resolved "https://registry.yarnpkg.com/@angular-eslint/eslint-plugin/-/eslint-plugin-18.4.3.tgz#7618bc6056086a98ed4d888f31185fc62e6be2d1" + integrity sha512-AyJbupiwTBR81P6T59v+aULEnPpZBCBxL2S5QFWfAhNCwWhcof4GihvdK2Z87yhvzDGeAzUFSWl/beJfeFa+PA== dependencies: - "@angular-eslint/bundled-angular-compiler" "18.4.2" - "@angular-eslint/utils" "18.4.2" + "@angular-eslint/bundled-angular-compiler" "18.4.3" + "@angular-eslint/utils" "18.4.3" -"@angular-eslint/schematics@18.4.2": - version "18.4.2" - resolved "https://registry.yarnpkg.com/@angular-eslint/schematics/-/schematics-18.4.2.tgz#2dceb682958faceaf7bdf48cf5484d1546544d56" - integrity sha512-pZCc3NhfwRT5S0DGXTzKbl3dD4I8K4LRYot+Aq4rzY5LtiGHDSi4PKu2M0OBSRrQFQXq7/2gDXGO0AvH6LX97w== +"@angular-eslint/schematics@18.4.3": + version "18.4.3" + resolved "https://registry.yarnpkg.com/@angular-eslint/schematics/-/schematics-18.4.3.tgz#1d6e9026e0054d556c37750ccff0ecce701561c1" + integrity sha512-D5maKn5e6n58+8n7jLFLD4g+RGPOPeDSsvPc1sqial5tEKLxAJQJS9WZ28oef3bhkob6C60D+1H0mMmEEVvyVA== dependencies: - "@angular-eslint/eslint-plugin" "18.4.2" - "@angular-eslint/eslint-plugin-template" "18.4.2" + "@angular-devkit/core" ">= 18.0.0 < 19.0.0" + "@angular-devkit/schematics" ">= 18.0.0 < 19.0.0" + "@angular-eslint/eslint-plugin" "18.4.3" + "@angular-eslint/eslint-plugin-template" "18.4.3" ignore "6.0.2" semver "7.6.3" strip-json-comments "3.1.1" -"@angular-eslint/template-parser@18.4.2": - version "18.4.2" - resolved "https://registry.yarnpkg.com/@angular-eslint/template-parser/-/template-parser-18.4.2.tgz#9e7d75b53ac8c50bfffeb01dadb297846ebbbed8" - integrity sha512-KGjDLUxMsdjaxC+8VTxCG07Q6qshOTWMYTvp2LZ4QBySDQnQuFwsIJIJfU8jJwzJCkPKfVpnyuHggAn7fdYnxA== +"@angular-eslint/template-parser@18.4.3": + version "18.4.3" + resolved "https://registry.yarnpkg.com/@angular-eslint/template-parser/-/template-parser-18.4.3.tgz#2c6c396563a278a6f2dfdb3fbe9d4310ad0c6dc6" + integrity sha512-JZMPtEB8yNip3kg4WDEWQyObSo2Hwf+opq2ElYuwe85GQkGhfJSJ2CQYo4FSwd+c5MUQAqESNRg9QqGYauDsiw== dependencies: - "@angular-eslint/bundled-angular-compiler" "18.4.2" + "@angular-eslint/bundled-angular-compiler" "18.4.3" eslint-scope "^8.0.2" -"@angular-eslint/utils@18.4.2": - version "18.4.2" - resolved "https://registry.yarnpkg.com/@angular-eslint/utils/-/utils-18.4.2.tgz#65486d64ba0a6f67fa6c9658812492ed74a1a2e2" - integrity sha512-+c0r33QSkAnGmu/DYAPfzJJk5QDX4TP2d6EFtsenrufqRkZqrOcK4Q5t61J92Ukkr03XoqTzTDSBjlwAfM56Rw== +"@angular-eslint/utils@18.4.3": + version "18.4.3" + resolved "https://registry.yarnpkg.com/@angular-eslint/utils/-/utils-18.4.3.tgz#1ad0558b21aaa987ce69604a7624d4b213e84d8c" + integrity sha512-w0bJ9+ELAEiPBSTPPm9bvDngfu1d8JbzUhvs2vU+z7sIz/HMwUZT5S4naypj2kNN0gZYGYrW0lt+HIbW87zTAQ== dependencies: - "@angular-eslint/bundled-angular-compiler" "18.4.2" + "@angular-eslint/bundled-angular-compiler" "18.4.3" "@angular/animations@18.2.13": version "18.2.13" @@ -1631,18 +1636,20 @@ integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== "@eslint/config-array@^0.19.0": - version "0.19.0" - resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.19.0.tgz#3251a528998de914d59bb21ba4c11767cf1b3519" - integrity sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ== + version "0.19.1" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.19.1.tgz#734aaea2c40be22bbb1f2a9dac687c57a6a4c984" + integrity sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA== dependencies: - "@eslint/object-schema" "^2.1.4" + "@eslint/object-schema" "^2.1.5" debug "^4.3.1" minimatch "^3.1.2" "@eslint/core@^0.9.0": - version "0.9.0" - resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.9.0.tgz#168ee076f94b152c01ca416c3e5cf82290ab4fcd" - integrity sha512-7ATR9F0e4W85D/0w7cU0SNj7qkAexMG+bAHEZOjo9akvGuhHE2m7umzWzfnpa0XAg5Kxc1BWmtPMV67jJ+9VUg== + version "0.9.1" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.9.1.tgz#31763847308ef6b7084a4505573ac9402c51f9d1" + integrity sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q== + dependencies: + "@types/json-schema" "^7.0.15" "@eslint/eslintrc@^3.2.0": version "3.2.0" @@ -1659,20 +1666,20 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@9.15.0": - version "9.15.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.15.0.tgz#df0e24fe869143b59731942128c19938fdbadfb5" - integrity sha512-tMTqrY+EzbXmKJR5ToI8lxu7jaN5EdmrBFJpQk5JmSlyLsx6o4t27r883K5xsLuCYCpfKBCGswMSWXsM+jB7lg== +"@eslint/js@9.17.0": + version "9.17.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.17.0.tgz#1523e586791f80376a6f8398a3964455ecc651ec" + integrity sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w== -"@eslint/object-schema@^2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.4.tgz#9e69f8bb4031e11df79e03db09f9dbbae1740843" - integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ== +"@eslint/object-schema@^2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.5.tgz#8670a8f6258a2be5b2c620ff314a1d984c23eb2e" + integrity sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ== "@eslint/plugin-kit@^0.2.3": - version "0.2.3" - resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz#812980a6a41ecf3a8341719f92a6d1e784a2e0e8" - integrity sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA== + version "0.2.4" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz#2b78e7bb3755784bb13faa8932a1d994d6537792" + integrity sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg== dependencies: levn "^0.4.1" @@ -3235,62 +3242,62 @@ dependencies: "@types/node" "*" -"@typescript-eslint/eslint-plugin@^8.16.0": - version "8.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.16.0.tgz#ac56825bcdf3b392fc76a94b1315d4a162f201a6" - integrity sha512-5YTHKV8MYlyMI6BaEG7crQ9BhSc8RxzshOReKwZwRWN0+XvvTOm+L/UYLCYxFpfwYuAAqhxiq4yae0CMFwbL7Q== +"@typescript-eslint/eslint-plugin@8.18.1": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.18.1.tgz#992e5ac1553ce20d0d46aa6eccd79dc36dedc805" + integrity sha512-Ncvsq5CT3Gvh+uJG0Lwlho6suwDfUXH0HztslDf5I+F2wAFAZMRwYLEorumpKLzmO2suAXZ/td1tBg4NZIi9CQ== dependencies: "@eslint-community/regexpp" "^4.10.0" - "@typescript-eslint/scope-manager" "8.16.0" - "@typescript-eslint/type-utils" "8.16.0" - "@typescript-eslint/utils" "8.16.0" - "@typescript-eslint/visitor-keys" "8.16.0" + "@typescript-eslint/scope-manager" "8.18.1" + "@typescript-eslint/type-utils" "8.18.1" + "@typescript-eslint/utils" "8.18.1" + "@typescript-eslint/visitor-keys" "8.18.1" graphemer "^1.4.0" ignore "^5.3.1" natural-compare "^1.4.0" ts-api-utils "^1.3.0" -"@typescript-eslint/parser@^8.16.0": - version "8.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.16.0.tgz#ee5b2d6241c1ab3e2e53f03fd5a32d8e266d8e06" - integrity sha512-D7DbgGFtsqIPIFMPJwCad9Gfi/hC0PWErRRHFnaCWoEDYi5tQUDiJCTmGUbBiLzjqAck4KcXt9Ayj0CNlIrF+w== +"@typescript-eslint/parser@8.18.1": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.18.1.tgz#c258bae062778b7696793bc492249027a39dfb95" + integrity sha512-rBnTWHCdbYM2lh7hjyXqxk70wvon3p2FyaniZuey5TrcGBpfhVp0OxOa6gxr9Q9YhZFKyfbEnxc24ZnVbbUkCA== dependencies: - "@typescript-eslint/scope-manager" "8.16.0" - "@typescript-eslint/types" "8.16.0" - "@typescript-eslint/typescript-estree" "8.16.0" - "@typescript-eslint/visitor-keys" "8.16.0" + "@typescript-eslint/scope-manager" "8.18.1" + "@typescript-eslint/types" "8.18.1" + "@typescript-eslint/typescript-estree" "8.18.1" + "@typescript-eslint/visitor-keys" "8.18.1" debug "^4.3.4" -"@typescript-eslint/scope-manager@8.16.0": - version "8.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.16.0.tgz#ebc9a3b399a69a6052f3d88174456dd399ef5905" - integrity sha512-mwsZWubQvBki2t5565uxF0EYvG+FwdFb8bMtDuGQLdCCnGPrDEDvm1gtfynuKlnpzeBRqdFCkMf9jg1fnAK8sg== +"@typescript-eslint/scope-manager@8.18.1": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.18.1.tgz#52cedc3a8178d7464a70beffed3203678648e55b" + integrity sha512-HxfHo2b090M5s2+/9Z3gkBhI6xBH8OJCFjH9MhQ+nnoZqxU3wNxkLT+VWXWSFWc3UF3Z+CfPAyqdCTdoXtDPCQ== dependencies: - "@typescript-eslint/types" "8.16.0" - "@typescript-eslint/visitor-keys" "8.16.0" + "@typescript-eslint/types" "8.18.1" + "@typescript-eslint/visitor-keys" "8.18.1" -"@typescript-eslint/type-utils@8.16.0": - version "8.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.16.0.tgz#585388735f7ac390f07c885845c3d185d1b64740" - integrity sha512-IqZHGG+g1XCWX9NyqnI/0CX5LL8/18awQqmkZSl2ynn8F76j579dByc0jhfVSnSnhf7zv76mKBQv9HQFKvDCgg== +"@typescript-eslint/type-utils@8.18.1": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.18.1.tgz#10f41285475c0bdee452b79ff7223f0e43a7781e" + integrity sha512-jAhTdK/Qx2NJPNOTxXpMwlOiSymtR2j283TtPqXkKBdH8OAMmhiUfP0kJjc/qSE51Xrq02Gj9NY7MwK+UxVwHQ== dependencies: - "@typescript-eslint/typescript-estree" "8.16.0" - "@typescript-eslint/utils" "8.16.0" + "@typescript-eslint/typescript-estree" "8.18.1" + "@typescript-eslint/utils" "8.18.1" debug "^4.3.4" ts-api-utils "^1.3.0" -"@typescript-eslint/types@8.16.0", "@typescript-eslint/types@^8.16.0": - version "8.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.16.0.tgz#49c92ae1b57942458ab83d9ec7ccab3005e64737" - integrity sha512-NzrHj6thBAOSE4d9bsuRNMvk+BvaQvmY4dDglgkgGC0EW/tB3Kelnp3tAKH87GEwzoxgeQn9fNGRyFJM/xd+GQ== +"@typescript-eslint/types@8.18.1", "@typescript-eslint/types@^8.0.0": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.18.1.tgz#d7f4f94d0bba9ebd088de840266fcd45408a8fff" + integrity sha512-7uoAUsCj66qdNQNpH2G8MyTFlgerum8ubf21s3TSM3XmKXuIn+H2Sifh/ES2nPOPiYSRJWAk0fDkW0APBWcpfw== -"@typescript-eslint/typescript-estree@8.16.0": - version "8.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.16.0.tgz#9d741e56e5b13469b5190e763432ce5551a9300c" - integrity sha512-E2+9IzzXMc1iaBy9zmo+UYvluE3TW7bCGWSF41hVWUE01o8nzr1rvOQYSxelxr6StUvRcTMe633eY8mXASMaNw== +"@typescript-eslint/typescript-estree@8.18.1": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.18.1.tgz#2a86cd64b211a742f78dfa7e6f4860413475367e" + integrity sha512-z8U21WI5txzl2XYOW7i9hJhxoKKNG1kcU4RzyNvKrdZDmbjkmLBo8bgeiOJmA06kizLI76/CCBAAGlTlEeUfyg== dependencies: - "@typescript-eslint/types" "8.16.0" - "@typescript-eslint/visitor-keys" "8.16.0" + "@typescript-eslint/types" "8.18.1" + "@typescript-eslint/visitor-keys" "8.18.1" debug "^4.3.4" fast-glob "^3.3.2" is-glob "^4.0.3" @@ -3298,22 +3305,22 @@ semver "^7.6.0" ts-api-utils "^1.3.0" -"@typescript-eslint/utils@8.16.0", "@typescript-eslint/utils@^8.16.0": - version "8.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.16.0.tgz#c71264c437157feaa97842809836254a6fc833c3" - integrity sha512-C1zRy/mOL8Pj157GiX4kaw7iyRLKfJXBR3L82hk5kS/GyHcOFmy4YUq/zfZti72I9wnuQtA/+xzft4wCC8PJdA== +"@typescript-eslint/utils@8.18.1", "@typescript-eslint/utils@^8.0.0": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.18.1.tgz#c4199ea23fc823c736e2c96fd07b1f7235fa92d5" + integrity sha512-8vikiIj2ebrC4WRdcAdDcmnu9Q/MXXwg+STf40BVfT8exDqBCUPdypvzcUPxEqRGKg9ALagZ0UWcYCtn+4W2iQ== dependencies: "@eslint-community/eslint-utils" "^4.4.0" - "@typescript-eslint/scope-manager" "8.16.0" - "@typescript-eslint/types" "8.16.0" - "@typescript-eslint/typescript-estree" "8.16.0" + "@typescript-eslint/scope-manager" "8.18.1" + "@typescript-eslint/types" "8.18.1" + "@typescript-eslint/typescript-estree" "8.18.1" -"@typescript-eslint/visitor-keys@8.16.0": - version "8.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.16.0.tgz#d5086afc060b01ff7a4ecab8d49d13d5a7b07705" - integrity sha512-pq19gbaMOmFE3CbL0ZB8J8BFCo2ckfHBfaIsaOZgBIF4EoISJIdLX5xRhd0FGB0LlHReNRuzoJoMGpTjq8F2CQ== +"@typescript-eslint/visitor-keys@8.18.1": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.18.1.tgz#344b4f6bc83f104f514676facf3129260df7610a" + integrity sha512-Vj0WLm5/ZsD013YeUKn+K0y8p1M0jPpxOkKdbD1wB0ns53a5piVY02zjf072TblEweAbcYiFiPoSMF3kp+VhhQ== dependencies: - "@typescript-eslint/types" "8.16.0" + "@typescript-eslint/types" "8.18.1" eslint-visitor-keys "^4.2.0" "@vitejs/plugin-basic-ssl@1.1.0": @@ -3573,6 +3580,21 @@ ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +angular-eslint@~18.4.3: + version "18.4.3" + resolved "https://registry.yarnpkg.com/angular-eslint/-/angular-eslint-18.4.3.tgz#d149df075304af9d4f57661c6886b0e1135c2f2b" + integrity sha512-0ZjLzzADGRLUhZC8ZpwSo6CE/m6QhQB/oljMJ0mEfP+lB1sy1v8PBKNsJboIcfEEgGW669Z/efVQ3df88yJLYg== + dependencies: + "@angular-devkit/core" ">= 18.0.0 < 19.0.0" + "@angular-devkit/schematics" ">= 18.0.0 < 19.0.0" + "@angular-eslint/builder" "18.4.3" + "@angular-eslint/eslint-plugin" "18.4.3" + "@angular-eslint/eslint-plugin-template" "18.4.3" + "@angular-eslint/schematics" "18.4.3" + "@angular-eslint/template-parser" "18.4.3" + "@typescript-eslint/types" "^8.0.0" + "@typescript-eslint/utils" "^8.0.0" + angular-gridster2@~18.0.1: version "18.0.1" resolved "https://registry.yarnpkg.com/angular-gridster2/-/angular-gridster2-18.0.1.tgz#ad04eff2c05aa693fe892ee66b6aa5e956e4dd13" @@ -4483,7 +4505,7 @@ critters@0.0.24: postcss "^8.4.23" postcss-media-query-parser "^0.2.3" -cross-spawn@^7.0.0, cross-spawn@^7.0.3, cross-spawn@^7.0.5: +cross-spawn@^7.0.0, cross-spawn@^7.0.3, cross-spawn@^7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== @@ -5474,17 +5496,17 @@ eslint-visitor-keys@^4.2.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== -eslint@~9.15.0: - version "9.15.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.15.0.tgz#77c684a4e980e82135ebff8ee8f0a9106ce6b8a6" - integrity sha512-7CrWySmIibCgT1Os28lUU6upBshZ+GxybLOrmRzi08kS8MBuO8QA7pXEgYgY5W8vK3e74xv0lpjo9DbaGU9Rkw== +eslint@~9.17.0: + version "9.17.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.17.0.tgz#faa1facb5dd042172fdc520106984b5c2421bb0c" + integrity sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.12.1" "@eslint/config-array" "^0.19.0" "@eslint/core" "^0.9.0" "@eslint/eslintrc" "^3.2.0" - "@eslint/js" "9.15.0" + "@eslint/js" "9.17.0" "@eslint/plugin-kit" "^0.2.3" "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" @@ -5493,7 +5515,7 @@ eslint@~9.15.0: "@types/json-schema" "^7.0.15" ajv "^6.12.4" chalk "^4.0.0" - cross-spawn "^7.0.5" + cross-spawn "^7.0.6" debug "^4.3.2" escape-string-regexp "^4.0.0" eslint-scope "^8.2.0" @@ -10020,6 +10042,15 @@ typeface-roboto@^1.1.13: resolved "https://registry.yarnpkg.com/typeface-roboto/-/typeface-roboto-1.1.13.tgz#9c4517cb91e311706c74823e857b4bac9a764ae5" integrity sha512-YXvbd3a1QTREoD+FJoEkl0VQNJoEjewR2H11IjVv4bp6ahuIcw0yyw/3udC4vJkHw3T3cUh85FTg8eWef3pSaw== +typescript-eslint@^8.18.1: + version "8.18.1" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.18.1.tgz#197b284b6769678ed77d9868df180eeaf61108eb" + integrity sha512-Mlaw6yxuaDEPQvb/2Qwu3/TfgeBHy9iTJ3mTwe7OvpPmF6KPQjVOfGyEJpPv6Ez2C34OODChhXrzYw/9phI0MQ== + dependencies: + "@typescript-eslint/eslint-plugin" "8.18.1" + "@typescript-eslint/parser" "8.18.1" + "@typescript-eslint/utils" "8.18.1" + typescript@~5.5.4: version "5.5.4" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba" From 39abbacb20492b98c8451aebae5ae78b3d8c5b3a Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 23 Dec 2024 12:57:08 +0200 Subject: [PATCH 025/108] UI: Remove json form component. Remove all react dependencies. --- ui-ngx/angular.json | 2 - ui-ngx/package.json | 20 - ui-ngx/src/app/core/http/widget.service.ts | 3 +- ui-ngx/src/app/core/services/utils.service.ts | 15 +- ui-ngx/src/app/modules/common/modules-map.ts | 2 - .../add-widget-dialog.component.ts | 39 +- .../dashboard-page/edit-widget.component.ts | 40 +- .../aggregated-data-key-row.component.ts | 8 +- .../basic/common/data-key-row.component.ts | 22 +- .../basic/common/data-keys-panel.component.ts | 9 +- .../data-key-config-dialog.component.html | 2 +- .../data-key-config-dialog.component.ts | 7 +- .../config/data-key-config.component.ts | 15 +- .../config/data-keys.component.models.ts | 5 +- .../widget/config/data-keys.component.ts | 11 +- .../widget/config/datasource.component.html | 4 +- .../widget/config/datasource.component.ts | 13 +- .../widget/config/datasources.component.ts | 13 +- .../config/widget-config.component.models.ts | 2 +- .../config/widget-settings.component.html | 3 - .../config/widget-settings.component.ts | 28 +- .../components/widget/lib/maps/map-widget2.ts | 13 +- .../widget/widget-component.service.ts | 64 +- .../widget/widget-config.component.html | 15 - .../widget/widget-config.component.ts | 70 +- .../home/models/widget-component.models.ts | 34 +- .../pages/widget/widget-editor.component.html | 62 +- .../pages/widget/widget-editor.component.ts | 115 +-- .../widget/widget-library-routing.module.ts | 18 +- .../json-form/json-form-component.models.ts | 23 - .../json-form/json-form.component.html | 23 - .../json-form/json-form.component.scss | 20 - .../json-form/json-form.component.ts | 293 ------- .../json-form/react/json-form-ace-editor.tsx | 239 ------ .../json-form/react/json-form-array.tsx | 177 ----- .../react/json-form-base-component.tsx | 122 --- .../json-form/react/json-form-checkbox.tsx | 46 -- .../json-form/react/json-form-color.tsx | 186 ----- .../json-form/react/json-form-css.tsx | 40 - .../json-form/react/json-form-date.tsx | 83 -- .../json-form/react/json-form-fieldset.tsx | 44 -- .../json-form/react/json-form-help.tsx | 27 - .../json-form/react/json-form-html.tsx | 40 - .../json-form/react/json-form-icon.tsx | 162 ---- .../json-form/react/json-form-image.tsx | 108 --- .../json-form/react/json-form-javascript.tsx | 40 - .../json-form/react/json-form-json.tsx | 40 - .../json-form/react/json-form-markdown.tsx | 33 - .../json-form/react/json-form-number.tsx | 99 --- .../json-form/react/json-form-radios.tsx | 51 -- .../json-form/react/json-form-rc-select.tsx | 202 ----- .../json-form/react/json-form-react.tsx | 53 -- .../json-form/react/json-form-schema-form.tsx | 217 ------ .../json-form/react/json-form-select.tsx | 93 --- .../json-form/react/json-form-text.tsx | 92 --- .../json-form/react/json-form.models.ts | 141 ---- .../components/json-form/react/json-form.scss | 361 --------- .../react/styles/thingsboardTheme.ts | 46 -- .../react => legacy}/json-form-utils.ts | 203 +---- .../src/app/shared/legacy/json-form.models.ts | 88 +++ .../app/shared/models/dynamic-form.models.ts | 48 +- ui-ngx/src/app/shared/models/widget.models.ts | 54 +- ui-ngx/src/app/shared/shared.module.ts | 3 - .../assets/locale/locale.constant-en_US.json | 5 +- ui-ngx/src/styles.scss | 4 + ui-ngx/src/tsconfig.app.json | 2 +- ui-ngx/tsconfig.json | 1 - ui-ngx/yarn.lock | 730 +----------------- 68 files changed, 365 insertions(+), 4528 deletions(-) delete mode 100644 ui-ngx/src/app/shared/components/json-form/json-form-component.models.ts delete mode 100644 ui-ngx/src/app/shared/components/json-form/json-form.component.html delete mode 100644 ui-ngx/src/app/shared/components/json-form/json-form.component.scss delete mode 100644 ui-ngx/src/app/shared/components/json-form/json-form.component.ts delete mode 100644 ui-ngx/src/app/shared/components/json-form/react/json-form-ace-editor.tsx delete mode 100644 ui-ngx/src/app/shared/components/json-form/react/json-form-array.tsx delete mode 100644 ui-ngx/src/app/shared/components/json-form/react/json-form-base-component.tsx delete mode 100644 ui-ngx/src/app/shared/components/json-form/react/json-form-checkbox.tsx delete mode 100644 ui-ngx/src/app/shared/components/json-form/react/json-form-color.tsx delete mode 100644 ui-ngx/src/app/shared/components/json-form/react/json-form-css.tsx delete mode 100644 ui-ngx/src/app/shared/components/json-form/react/json-form-date.tsx delete mode 100644 ui-ngx/src/app/shared/components/json-form/react/json-form-fieldset.tsx delete mode 100644 ui-ngx/src/app/shared/components/json-form/react/json-form-help.tsx delete mode 100644 ui-ngx/src/app/shared/components/json-form/react/json-form-html.tsx delete mode 100644 ui-ngx/src/app/shared/components/json-form/react/json-form-icon.tsx delete mode 100644 ui-ngx/src/app/shared/components/json-form/react/json-form-image.tsx delete mode 100644 ui-ngx/src/app/shared/components/json-form/react/json-form-javascript.tsx delete mode 100644 ui-ngx/src/app/shared/components/json-form/react/json-form-json.tsx delete mode 100644 ui-ngx/src/app/shared/components/json-form/react/json-form-markdown.tsx delete mode 100644 ui-ngx/src/app/shared/components/json-form/react/json-form-number.tsx delete mode 100644 ui-ngx/src/app/shared/components/json-form/react/json-form-radios.tsx delete mode 100644 ui-ngx/src/app/shared/components/json-form/react/json-form-rc-select.tsx delete mode 100644 ui-ngx/src/app/shared/components/json-form/react/json-form-react.tsx delete mode 100644 ui-ngx/src/app/shared/components/json-form/react/json-form-schema-form.tsx delete mode 100644 ui-ngx/src/app/shared/components/json-form/react/json-form-select.tsx delete mode 100644 ui-ngx/src/app/shared/components/json-form/react/json-form-text.tsx delete mode 100644 ui-ngx/src/app/shared/components/json-form/react/json-form.models.ts delete mode 100644 ui-ngx/src/app/shared/components/json-form/react/json-form.scss delete mode 100644 ui-ngx/src/app/shared/components/json-form/react/styles/thingsboardTheme.ts rename ui-ngx/src/app/shared/{components/json-form/react => legacy}/json-form-utils.ts (70%) create mode 100644 ui-ngx/src/app/shared/legacy/json-form.models.ts diff --git a/ui-ngx/angular.json b/ui-ngx/angular.json index f1efd8943a..f605bfee8a 100644 --- a/ui-ngx/angular.json +++ b/ui-ngx/angular.json @@ -99,8 +99,6 @@ "node_modules/jquery.terminal/css/jquery.terminal.min.css", "node_modules/tooltipster/dist/css/tooltipster.bundle.min.css", "node_modules/tooltipster/dist/css/plugins/tooltipster/sideTip/themes/tooltipster-sideTip-shadow.min.css", - "src/app/shared/components/json-form/react/json-form.scss", - "node_modules/rc-select/assets/index.less", "node_modules/jstree-bootstrap-theme/dist/themes/proton/style.min.css", "node_modules/leaflet/dist/leaflet.css", "src/app/modules/home/components/widget/lib/maps/markers.scss", diff --git a/ui-ngx/package.json b/ui-ngx/package.json index d8870a3568..85c00e0a2e 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -25,8 +25,6 @@ "@angular/platform-browser-dynamic": "18.2.13", "@angular/router": "18.2.13", "@auth0/angular-jwt": "^5.2.0", - "@emotion/react": "11.13.3", - "@emotion/styled": "11.13.0", "@flowjs/flow.js": "^2.14.1", "@flowjs/ngx-flow": "18.0.1", "@geoman-io/leaflet-geoman-free": "2.17.0", @@ -34,12 +32,6 @@ "@mat-datetimepicker/core": "~14.0.0", "@mdi/svg": "^7.4.47", "@messageformat/core": "^3.4.0", - "@mui/icons-material": "6.1.2", - "@mui/lab": "6.0.0-beta.10", - "@mui/material": "6.1.2", - "@mui/styles": "6.1.2", - "@mui/system": "6.1.2", - "@mui/x-date-pickers": "7.18.0", "@ngrx/effects": "^18.1.1", "@ngrx/store": "^18.1.1", "@ngrx/store-devtools": "^18.1.1", @@ -85,16 +77,8 @@ "ngx-sharebuttons": "^15.0.6", "ngx-translate-messageformat-compiler": "^7.0.0", "objectpath": "^2.0.0", - "prettier": "^2.8.3", - "prop-types": "^15.8.1", "qrcode": "^1.5.4", "raphael": "^2.3.0", - "rc-select": "14.15.2", - "react": "18.3.1", - "react-ace": "12.0.0", - "react-dom": "18.3.1", - "react-dropzone": "14.2.9", - "reactcss": "^1.2.3", "rxjs": "~7.8.1", "schema-inspector": "^2.1.0", "screenfull": "^6.0.2", @@ -137,8 +121,6 @@ "@types/lodash": "^4.17.13", "@types/node": "~20.17.8", "@types/raphael": "^2.3.9", - "@types/react": "18.3.10", - "@types/react-dom": "18.3.0", "@types/systemjs": "6.15.1", "@types/tinycolor2": "^1.4.6", "@types/tooltipster": "^0.0.35", @@ -162,8 +144,6 @@ "typescript": "~5.5.4" }, "resolutions": { - "@types/react": "18.3.10", - "rc-virtual-list": "3.5.2", "ace-builds": "1.36.5", "tinymce": "6.8.5", "rollup": "4.22.4", diff --git a/ui-ngx/src/app/core/http/widget.service.ts b/ui-ngx/src/app/core/http/widget.service.ts index 0d3718ee12..ebf390866c 100644 --- a/ui-ngx/src/app/core/http/widget.service.ts +++ b/ui-ngx/src/app/core/http/widget.service.ts @@ -24,7 +24,7 @@ import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; import { BaseWidgetType, DeprecatedFilter, - fullWidgetTypeFqn, + fullWidgetTypeFqn, migrateWidgetTypeToDynamicForms, WidgetType, widgetType, WidgetTypeDetails, @@ -271,6 +271,7 @@ export class WidgetService { return this.getWidgetType(templateWidgetType.template.fullFqn, config).pipe( map((result) => { + result = migrateWidgetTypeToDynamicForms(result); const widgetInfo = toWidgetInfo(result); widgetInfo.fullFqn = undefined; return widgetInfo; diff --git a/ui-ngx/src/app/core/services/utils.service.ts b/ui-ngx/src/app/core/services/utils.service.ts index 79fde27014..90c2547e09 100644 --- a/ui-ngx/src/app/core/services/utils.service.ts +++ b/ui-ngx/src/app/core/services/utils.service.ts @@ -23,7 +23,6 @@ import { baseUrl, createLabelFromDatasource, deepClone, - deleteNullProperties, guid, hashCode, isDefined, @@ -41,7 +40,6 @@ import { DataKeyType, SharedTelemetrySubscriber } from '@app/shared/models/telem import { alarmFields, alarmSeverityTranslations, alarmStatusTranslations } from '@shared/models/alarm.models'; import { materialColors } from '@app/shared/models/material.models'; import { WidgetInfo } from '@home/models/widget-component.models'; -import jsonSchemaDefaults from 'json-schema-defaults'; import { Observable } from 'rxjs'; import { publishReplay, refCount } from 'rxjs/operators'; import { WidgetContext } from '@app/modules/home/models/widget-component.models'; @@ -51,6 +49,7 @@ import { DatePipe, DOCUMENT } from '@angular/common'; import { entityTypeTranslations } from '@shared/models/entity-type.models'; import cssjs from '@core/css/css'; import { isNotEmptyTbFunction } from '@shared/models/js-function.models'; +import { defaultFormProperties, FormProperty } from '@shared/models/dynamic-form.models'; const i18nRegExp = new RegExp(`{${i18nPrefix}:[^{}]+}`, 'g'); @@ -138,10 +137,10 @@ export class UtilsService { return predefinedFunctions[func]; } - public getDefaultDatasource(dataKeySchema: any): Datasource { + public getDefaultDatasource(dataKeyForm: FormProperty[]): Datasource { const datasource = deepClone(this.defaultDatasource); - if (isDefined(dataKeySchema)) { - datasource.dataKeys[0].settings = this.generateObjectFromJsonSchema(dataKeySchema); + if (dataKeyForm?.length) { + datasource.dataKeys[0].settings = defaultFormProperties(dataKeyForm); } return datasource; } @@ -189,12 +188,6 @@ export class UtilsService { return ''; } - public generateObjectFromJsonSchema(schema: any): any { - const obj = jsonSchemaDefaults(schema); - deleteNullProperties(obj); - return obj; - } - public processWidgetException(exception: any): ExceptionData { const data = this.parseException(exception, -6); if (data.message?.startsWith('NG0')) { diff --git a/ui-ngx/src/app/modules/common/modules-map.ts b/ui-ngx/src/app/modules/common/modules-map.ts index 0cc3f5bb2f..257e1bf348 100644 --- a/ui-ngx/src/app/modules/common/modules-map.ts +++ b/ui-ngx/src/app/modules/common/modules-map.ts @@ -160,7 +160,6 @@ import * as MaterialIconsDialogComponent from '@shared/components/dialog/materia import * as ColorInputComponent from '@shared/components/color-input.component'; import * as MaterialIconSelectComponent from '@shared/components/material-icon-select.component'; import * as NodeScriptTestDialogComponent from '@shared/components/dialog/node-script-test-dialog.component'; -import * as JsonFormComponent from '@shared/components/json-form/json-form.component'; import * as NotificationComponent from '@shared/components/notification/notification.component'; import * as TemplateAutocompleteComponent from '@shared/components/notification/template-autocomplete.component'; import * as ImageInputComponent from '@shared/components/image-input.component'; @@ -507,7 +506,6 @@ class ModulesMap implements IModulesMap { '@shared/components/color-input.component': ColorInputComponent, '@shared/components/material-icon-select.component': MaterialIconSelectComponent, '@shared/components/dialog/node-script-test-dialog.component': NodeScriptTestDialogComponent, - '@shared/components/json-form/json-form.component': JsonFormComponent, '@shared/components/notification/notification.component': NotificationComponent, '@shared/components/notification/template-autocomplete.component': TemplateAutocompleteComponent, '@shared/components/image-input.component': ImageInputComponent, diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.ts index 860825f5e8..fe96529a1d 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.ts @@ -26,11 +26,10 @@ import { Widget, WidgetConfigMode, widgetTypesData } from '@shared/models/widget import { Dashboard } from '@app/shared/models/dashboard.models'; import { IAliasController, IStateController } from '@core/api/widget-api.models'; import { WidgetConfigComponentData, WidgetInfo } from '@home/models/widget-component.models'; -import { isDefined, isDefinedAndNotNull, isString } from '@core/utils'; +import { isDefined, isDefinedAndNotNull } from '@core/utils'; import { TranslateService } from '@ngx-translate/core'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models'; -import { jsonFormSchemaToFormProperties } from '@shared/models/dynamic-form.models'; export interface AddWidgetDialogData { dashboard: Dashboard; @@ -102,34 +101,17 @@ export class AddWidgetDialogComponent extends DialogComponent !!key && !!key.type && !!key.name; @@ -197,16 +191,16 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan return this.widgetConfigComponent.aliasController; } - get dataKeySettingsSchema(): JsonSettingsSchema { - return this.widgetConfigComponent.modelValue?.dataKeySettingsSchema; + get dataKeySettingsForm(): FormProperty[] { + return this.widgetConfigComponent.modelValue?.dataKeySettingsForm; } get dataKeySettingsDirective(): string { return this.widgetConfigComponent.modelValue?.dataKeySettingsDirective; } - get latestDataKeySettingsSchema(): JsonSettingsSchema { - return this.widgetConfigComponent.modelValue?.latestDataKeySettingsSchema; + get latestDataKeySettingsForm(): FormProperty[] { + return this.widgetConfigComponent.modelValue?.latestDataKeySettingsForm; } get latestDataKeySettingsDirective(): string { @@ -325,7 +319,7 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan data: { dataKey: deepClone(this.modelValue), dataKeyConfigMode: advanced ? DataKeyConfigMode.advanced : DataKeyConfigMode.general, - dataKeySettingsSchema: this.isLatestDataKeys ? this.latestDataKeySettingsSchema : this.dataKeySettingsSchema, + dataKeySettingsForm: this.isLatestDataKeys ? this.latestDataKeySettingsForm : this.dataKeySettingsForm, dataKeySettingsDirective: this.isLatestDataKeys ? this.latestDataKeySettingsDirective : this.dataKeySettingsDirective, dashboard: this.dashboard, aliasController: this.aliasController, @@ -359,7 +353,7 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan } private _generateDataKey(key: DataKey): DataKey { - key = this.callbacks.generateDataKey(key.name, key.type, this.dataKeySettingsSchema, this.isLatestDataKeys, + key = this.callbacks.generateDataKey(key.name, key.type, this.dataKeySettingsForm, this.isLatestDataKeys, this.dataKeySettingsFunction); if (!this.keyRowFormGroup.get('label').value) { this.keyRowFormGroup.get('label').patchValue(key.label, {emitEvent: false}); diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.ts index 67ed19b2d2..c66ce1a289 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.ts @@ -37,7 +37,7 @@ import { } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; -import { DataKey, DatasourceType, JsonSettingsSchema, widgetType } from '@shared/models/widget.models'; +import { DataKey, DatasourceType, widgetType } from '@shared/models/widget.models'; import { dataKeyRowValidator, dataKeyValid } from '@home/components/widget/config/basic/common/data-key-row.component'; import { CdkDragDrop } from '@angular/cdk/drag-drop'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; @@ -45,6 +45,7 @@ import { UtilsService } from '@core/services/utils.service'; import { DataKeysCallbacks, DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models'; import { coerceBoolean } from '@shared/decorators/coercion'; import { TimeSeriesChartYAxisId } from '@home/components/widget/lib/chart/time-series-chart.models'; +import { FormProperty } from '@shared/models/dynamic-form.models'; @Component({ selector: 'tb-data-keys-panel', @@ -154,8 +155,8 @@ export class DataKeysPanelComponent implements ControlValueAccessor, OnInit, OnC this.widgetConfigComponent.modelValue?.typeParameters?.hasAdditionalLatestDataKeys; } - get datakeySettingsSchema(): JsonSettingsSchema { - return this.widgetConfigComponent.modelValue?.dataKeySettingsSchema; + get dataKeySettingsForm(): FormProperty[] { + return this.widgetConfigComponent.modelValue?.dataKeySettingsForm; } get dataKeySettingsFunction(): DataKeySettingsFunction { @@ -276,7 +277,7 @@ export class DataKeysPanelComponent implements ControlValueAccessor, OnInit, OnC } addKey() { - const dataKey = this.callbacks.generateDataKey('', null, this.datakeySettingsSchema, + const dataKey = this.callbacks.generateDataKey('', null, this.dataKeySettingsForm, false, this.dataKeySettingsFunction); dataKey.label = ''; dataKey.decimals = 0; diff --git a/ui-ngx/src/app/modules/home/components/widget/config/data-key-config-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/config/data-key-config-dialog.component.html index 4d5f38d30e..ca5ae93a84 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/data-key-config-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/data-key-config-dialog.component.html @@ -35,7 +35,7 @@
; private functionTypeKeys: Array; @@ -226,15 +227,11 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con type: DataKeyType.function }); } - if (this.dataKeySettingsSchema && this.dataKeySettingsSchema.schema || + if (this.dataKeySettingsForm?.length || this.dataKeySettingsDirective && this.dataKeySettingsDirective.length) { this.hasAdvanced = true; this.dataKeySettingsData = { - schema: this.dataKeySettingsSchema?.schema || { - type: 'object', - properties: {} - }, - form: this.dataKeySettingsSchema?.form || ['*'], + settingsForm: this.dataKeySettingsForm, settingsDirective: this.dataKeySettingsDirective }; this.dataKeySettingsFormGroup = this.fb.group({ diff --git a/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.models.ts b/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.models.ts index b0e80973f0..a0c211f32f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.models.ts @@ -15,13 +15,14 @@ /// import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; -import { DataKey, JsonSettingsSchema } from '@shared/models/widget.models'; +import { DataKey } from '@shared/models/widget.models'; import { Observable } from 'rxjs'; +import { FormProperty } from '@shared/models/dynamic-form.models'; export type DataKeySettingsFunction = (key: DataKey, isLatestDataKey: boolean) => any; export interface DataKeysCallbacks { - generateDataKey: (chip: any, type: DataKeyType, datakeySettingsSchema: JsonSettingsSchema, + generateDataKey: (chip: any, type: DataKeyType, dataKeySettingsForm: FormProperty[], isLatestDataKey: boolean, dataKeySettingsFunction: DataKeySettingsFunction) => DataKey; fetchEntityKeys: (entityAliasId: string, types: Array) => Observable>; fetchEntityKeysForDevice: (deviceId: string, types: Array) => Observable>; diff --git a/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.ts index e4f0b41500..bb1647faa4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.ts @@ -50,7 +50,7 @@ import { MatAutocomplete, MatAutocompleteTrigger } from '@angular/material/autoc import { MatChipGrid, MatChipInputEvent, MatChipRow } from '@angular/material/chips'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; -import { DataKey, DatasourceType, JsonSettingsSchema, Widget, widgetType } from '@shared/models/widget.models'; +import { DataKey, DatasourceType, Widget, widgetType } from '@shared/models/widget.models'; import { IAliasController } from '@core/api/widget-api.models'; import { DataKeySettingsFunction } from './data-keys.component.models'; import { alarmFields } from '@shared/models/alarm.models'; @@ -72,6 +72,7 @@ import { ColorPickerPanelComponent } from '@shared/components/color-picker/color import { TbPopoverService } from '@shared/components/popover.service'; import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-config.component.models'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { FormProperty } from '@shared/models/dynamic-form.models'; @Component({ selector: 'tb-data-keys', @@ -151,7 +152,7 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange aliasController: IAliasController; @Input() - datakeySettingsSchema: JsonSettingsSchema; + dataKeySettingsForm: FormProperty[]; @Input() datakeySettingsFunction: DataKeySettingsFunction; @@ -375,7 +376,7 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange if (this.widgetType === widgetType.alarm) { this.keys = this.utils.getDefaultAlarmDataKeys(); } else if (this.isCountDatasource) { - this.keys = [this.callbacks.generateDataKey('count', DataKeyType.count, this.datakeySettingsSchema, + this.keys = [this.callbacks.generateDataKey('count', DataKeyType.count, this.dataKeySettingsForm, this.latestDataKeys, this.datakeySettingsFunction)]; } else { this.keys = []; @@ -462,7 +463,7 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange } private addFromChipValue(chip: DataKey) { - const key = this.callbacks.generateDataKey(chip.name, chip.type, this.datakeySettingsSchema, this.latestDataKeys, + const key = this.callbacks.generateDataKey(chip.name, chip.type, this.dataKeySettingsForm, this.latestDataKeys, this.datakeySettingsFunction); this.addKey(key); } @@ -562,7 +563,7 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], data: { dataKey: deepClone(key), - dataKeySettingsSchema: this.datakeySettingsSchema, + dataKeySettingsForm: this.dataKeySettingsForm, dataKeySettingsDirective: this.dataKeySettingsDirective, dashboard: this.dashboard, aliasController: this.aliasController, diff --git a/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html b/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html index 18eb6f7a23..8af7753525 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html @@ -70,7 +70,7 @@ [optDataKeys]="isDataKeysOptional(datasourceFormGroup.get('type').value)" [simpleDataKeysLabel]="!hasAdditionalLatestDataKeys" [aliasController]="aliasController" - [datakeySettingsSchema]="dataKeySettingsSchema" + [dataKeySettingsForm]="dataKeySettingsForm" [dataKeySettingsDirective]="dataKeySettingsDirective" [datakeySettingsFunction]="dataKeySettingsFunction" [dashboard]="dashboard" @@ -86,7 +86,7 @@ latestDataKeys [optDataKeys]="true" [aliasController]="aliasController" - [datakeySettingsSchema]="latestDataKeySettingsSchema" + [dataKeySettingsForm]="latestDataKeySettingsForm" [dataKeySettingsDirective]="latestDataKeySettingsDirective" [datakeySettingsFunction]="dataKeySettingsFunction" [dashboard]="dashboard" diff --git a/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.ts index 6038982d48..df7c6162fb 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.ts @@ -29,8 +29,8 @@ import { Datasource, DatasourceType, datasourceTypeTranslationMap, - JsonSettingsSchema, - Widget, WidgetConfigMode, + Widget, + WidgetConfigMode, widgetType } from '@shared/models/widget.models'; import { AlarmSearchStatus } from '@shared/models/alarm.models'; @@ -43,6 +43,7 @@ import { DataKeysCallbacks, DataKeySettingsFunction } from '@home/components/wid import { EntityType } from '@shared/models/entity-type.models'; import { DatasourcesComponent } from '@home/components/widget/config/datasources.component'; import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-config.component.models'; +import { FormProperty } from '@shared/models/dynamic-form.models'; @Component({ selector: 'tb-datasource', @@ -108,16 +109,16 @@ export class DatasourceComponent implements ControlValueAccessor, OnInit, Valida return this.widgetConfigComponent.modelValue?.typeParameters?.maxDataKeys; } - public get dataKeySettingsSchema(): JsonSettingsSchema { - return this.widgetConfigComponent.modelValue?.dataKeySettingsSchema; + public get dataKeySettingsForm(): FormProperty[] { + return this.widgetConfigComponent.modelValue?.dataKeySettingsForm; } public get dataKeySettingsDirective(): string { return this.widgetConfigComponent.modelValue?.dataKeySettingsDirective; } - public get latestDataKeySettingsSchema(): JsonSettingsSchema { - return this.widgetConfigComponent.modelValue?.latestDataKeySettingsSchema; + public get latestDataKeySettingsForm(): FormProperty[] { + return this.widgetConfigComponent.modelValue?.latestDataKeySettingsForm; } public get latestDataKeySettingsDirective(): string { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/datasources.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/datasources.component.ts index 6ad1972886..7b048d0945 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/datasources.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/datasources.component.ts @@ -30,8 +30,8 @@ import { import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { Datasource, - DatasourceType, datasourceValid, - JsonSettingsSchema, + DatasourceType, + datasourceValid, WidgetConfigMode, widgetType } from '@shared/models/widget.models'; @@ -42,6 +42,7 @@ import { UtilsService } from '@core/services/utils.service'; import { DataKeysCallbacks, DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models'; import { TranslateService } from '@ngx-translate/core'; import { coerceBoolean } from '@shared/decorators/coercion'; +import { FormProperty } from '@shared/models/dynamic-form.models'; @Component({ selector: 'tb-datasources', @@ -336,8 +337,8 @@ export class DatasourcesComponent implements ControlValueAccessor, OnInit, Valid public addDatasource(emitEvent = true) { let newDatasource: Datasource; if (this.widgetConfigComponent.functionsOnly) { - newDatasource = deepClone(this.utils.getDefaultDatasource(this.dataKeySettingsSchema.schema)); - newDatasource.dataKeys = [this.dataKeysCallbacks.generateDataKey('Sin', DataKeyType.function, this.dataKeySettingsSchema, + newDatasource = deepClone(this.utils.getDefaultDatasource(this.dataKeySettingsForm)); + newDatasource.dataKeys = [this.dataKeysCallbacks.generateDataKey('Sin', DataKeyType.function, this.dataKeySettingsForm, false, this.dataKeySettingsFunction)]; } else { const type = this.basicMode ? this.datasourcesMode : DatasourceType.entity; @@ -351,8 +352,8 @@ export class DatasourcesComponent implements ControlValueAccessor, OnInit, Valid this.datasourcesFormArray.push(this.fb.control(newDatasource, []), {emitEvent}); } - private get dataKeySettingsSchema(): JsonSettingsSchema { - return this.widgetConfigComponent.modelValue?.dataKeySettingsSchema; + private get dataKeySettingsForm(): FormProperty[] { + return this.widgetConfigComponent.modelValue?.dataKeySettingsForm; } private get dataKeySettingsFunction(): DataKeySettingsFunction { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts b/ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts index f3807d319d..07f8b1b501 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts @@ -202,7 +202,7 @@ export abstract class BasicWidgetConfigComponent extends PageComponent implement protected constructDataKey(configData: WidgetConfigComponentData, key: DataKey, isLatestKey: boolean): DataKey { const dataKey = this.widgetConfigComponent.widgetConfigCallbacks.generateDataKey(key.name, key.type, - configData.dataKeySettingsSchema, isLatestKey, configData.dataKeySettingsFunction); + configData.dataKeySettingsForm, isLatestKey, configData.dataKeySettingsFunction); if (key.label) { dataKey.label = key.label; } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.html index a0840b84e1..c3490e9def 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.html @@ -18,9 +18,6 @@
{{definedDirectiveError}}
- - diff --git a/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts index 6e1131cc3e..4743bff002 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts @@ -38,8 +38,6 @@ import { } from '@angular/forms'; import { Subscription } from 'rxjs'; import { TranslateService } from '@ngx-translate/core'; -import { JsonFormComponent } from '@shared/components/json-form/json-form.component'; -import { JsonFormComponentData } from '@shared/components/json-form/json-form-component.models'; import { DynamicFormData, IWidgetSettingsComponent, Widget, WidgetSettings } from '@shared/models/widget.models'; import { widgetSettingsComponentsMap } from '@home/components/widget/lib/settings/widget-settings.module'; import { Dashboard } from '@shared/models/dashboard.models'; @@ -99,7 +97,7 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnDestroy, private definedSettingsComponentRef: ComponentRef; private definedSettingsComponent: IWidgetSettingsComponent; - private widgetSettingsFormData: JsonFormComponentData | DynamicFormData; + private widgetSettingsFormData: DynamicFormData; private propagateChange = (_v: any) => { }; constructor(private translate: TranslateService, @@ -166,13 +164,9 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnDestroy, } } - writeValue(value: JsonFormComponentData | DynamicFormData): void { + writeValue(value: DynamicFormData): void { this.widgetSettingsFormData = value; - if ('settingsForm' in this.widgetSettingsFormData) { - this.settingsForm = this.widgetSettingsFormData.settingsForm; - } else { - this.settingsForm = null; - } + this.settingsForm = this.widgetSettingsFormData.settingsForm; if (this.changeSubscription) { this.changeSubscription.unsubscribe(); this.changeSubscription = null; @@ -187,12 +181,10 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnDestroy, this.updateModel(settings); }); } else { - const settingsValue = this.useJsonForm() ? this.widgetSettingsFormData : this.widgetSettingsFormData.model; - this.widgetSettingsFormGroup.get('settings').patchValue(settingsValue, {emitEvent: false}); + this.widgetSettingsFormGroup.get('settings').patchValue(this.widgetSettingsFormData.model, {emitEvent: false}); this.changeSubscription = this.widgetSettingsFormGroup.get('settings').valueChanges.subscribe( - (data: JsonFormComponentData | WidgetSettings) => { - const settings = this.useJsonForm() ? data.model : data; - this.updateModel(settings); + (data: WidgetSettings) => { + this.updateModel(data); } ); } @@ -203,12 +195,8 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnDestroy, this.settingsDirective.length && !this.definedDirectiveError; } - useJsonForm(): boolean { - return (!this.settingsDirective || !this.settingsDirective.length) && !this.settingsForm?.length; - } - useDynamicForm(): boolean { - return (!this.settingsDirective || !this.settingsDirective.length) && !!this.settingsForm?.length; + return !this.settingsDirective || !this.settingsDirective.length; } private updateModel(settings: WidgetSettings) { @@ -258,7 +246,7 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnDestroy, } }; } - } else if (this.useJsonForm() || this.useDynamicForm()) { + } else if (this.useDynamicForm()) { if (!this.widgetSettingsFormGroup.get('settings').valid) { return { widgetSettings: { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts index 8dea835e24..4ca5d4302a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts @@ -19,23 +19,18 @@ import LeafletMap from './leaflet-map'; import { MapWidgetInterface, MapWidgetStaticInterface } from './map-widget.interface'; import { WidgetContext } from '@app/modules/home/models/widget-component.models'; import { getDefCenterPosition, parseWithTranslation } from './common-maps-utils'; -import { - Datasource, - DatasourceData, - FormattedData, - JsonSettingsSchema, - WidgetActionDescriptor -} from '@shared/models/widget.models'; +import { Datasource, DatasourceData, FormattedData, WidgetActionDescriptor } from '@shared/models/widget.models'; import { TranslateService } from '@ngx-translate/core'; import { UtilsService } from '@core/services/utils.service'; import { EntityDataPageLink } from '@shared/models/query/query.models'; import { providerClass } from '@home/components/widget/lib/maps/providers/public-api'; -import { isDefined, isDefinedAndNotNull, parseFunction, parseTbFunction } from '@core/utils'; +import { isDefined, isDefinedAndNotNull, parseTbFunction } from '@core/utils'; import L from 'leaflet'; import { firstValueFrom, forkJoin, from, Observable, of } from 'rxjs'; import { AttributeService } from '@core/http/attribute.service'; import { EntityId } from '@shared/models/id/entity-id'; import { AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models'; +import { FormProperty } from '@shared/models/dynamic-form.models'; // @dynamic export class MapWidgetController implements MapWidgetInterface { @@ -113,7 +108,7 @@ export class MapWidgetController implements MapWidgetInterface { map: LeafletMap; provider: MapProviders; - schema: JsonSettingsSchema; + form: FormProperty[]; data: DatasourceData[]; settings: WidgetUnitedMapSettings; pageLink: EntityDataPageLink; diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts index 648491faed..d9b18d896a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts @@ -36,7 +36,7 @@ import { ResourcesService } from '@core/services/resources.service'; import { - IWidgetSettingsComponent, + IWidgetSettingsComponent, migrateWidgetTypeToDynamicForms, Widget, widgetActionSources, WidgetControllerDescriptor, @@ -63,6 +63,7 @@ import { basicWidgetConfigComponentsMap } from '@home/components/widget/config/b import { IBasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; import { compileTbFunction, TbFunction } from '@shared/models/js-function.models'; import { HttpClient } from '@angular/common/http'; +import { jsonFormSchemaToFormProperties } from '@shared/models/dynamic-form.models'; @Injectable() export class WidgetComponentService { @@ -113,9 +114,8 @@ export class WidgetComponentService { templateCss: this.utils.editWidgetInfo.templateCss, controllerScript: this.utils.editWidgetInfo.controllerScript, settingsForm: this.utils.editWidgetInfo.settingsForm, - settingsSchema: this.utils.editWidgetInfo.settingsSchema, - dataKeySettingsSchema: this.utils.editWidgetInfo.dataKeySettingsSchema, - latestDataKeySettingsSchema: this.utils.editWidgetInfo.latestDataKeySettingsSchema, + dataKeySettingsForm: this.utils.editWidgetInfo.dataKeySettingsForm, + latestDataKeySettingsForm: this.utils.editWidgetInfo.latestDataKeySettingsForm, settingsDirective: this.utils.editWidgetInfo.settingsDirective, dataKeySettingsDirective: this.utils.editWidgetInfo.dataKeySettingsDirective, latestDataKeySettingsDirective: this.utils.editWidgetInfo.latestDataKeySettingsDirective, @@ -276,6 +276,7 @@ export class WidgetComponentService { this.widgetsInfoFetchQueue.set(fullFqn, fetchQueue); this.widgetService.getWidgetType(fullFqn, {ignoreErrors: true}).subscribe( (widgetType) => { + widgetType = migrateWidgetTypeToDynamicForms(widgetType); this.loadWidget(widgetType, widgetInfoSubject); }, () => { @@ -302,14 +303,11 @@ export class WidgetComponentService { if (widgetControllerDescriptor.settingsForm) { widgetInfo.typeSettingsForm = widgetControllerDescriptor.settingsForm; } - if (widgetControllerDescriptor.settingsSchema) { - widgetInfo.typeSettingsSchema = widgetControllerDescriptor.settingsSchema; + if (widgetControllerDescriptor.dataKeySettingsForm) { + widgetInfo.typeDataKeySettingsForm = widgetControllerDescriptor.dataKeySettingsForm; } - if (widgetControllerDescriptor.dataKeySettingsSchema) { - widgetInfo.typeDataKeySettingsSchema = widgetControllerDescriptor.dataKeySettingsSchema; - } - if (widgetControllerDescriptor.latestDataKeySettingsSchema) { - widgetInfo.typeLatestDataKeySettingsSchema = widgetControllerDescriptor.latestDataKeySettingsSchema; + if (widgetControllerDescriptor.latestDataKeySettingsForm) { + widgetInfo.typeLatestDataKeySettingsForm = widgetControllerDescriptor.latestDataKeySettingsForm; } widgetInfo.typeParameters = widgetControllerDescriptor.typeParameters; widgetInfo.actionSources = widgetControllerDescriptor.actionSources; @@ -510,12 +508,23 @@ export class WidgetComponentService { ' }\n\n' + - ' self.getSettingsSchema = function() {\n\n' + - + ' self.getSettingsForm = function() {\n\n' + + return [ + { + 'id': 'testProp', + 'name': 'Test property', + 'type': 'text', + 'default': 'Default value' + } + ]; ' }\n\n' + - ' self.getDataKeySettingsSchema = function() {\n\n' + + ' self.getDataKeySettingsForm = function() {\n\n' + + return []; + ' }\n\n' + + ' self.getLatestDataKeySettingsForm = function() {\n\n' + + return []; ' }\n\n' + ' self.onDestroy = function() {\n\n' + @@ -546,15 +555,30 @@ export class WidgetComponentService { if (isFunction(widgetTypeInstance.getSettingsForm)) { result.settingsForm = widgetTypeInstance.getSettingsForm(); } - if (isFunction(widgetTypeInstance.getSettingsSchema)) { - result.settingsSchema = widgetTypeInstance.getSettingsSchema(); + if (isFunction(widgetTypeInstance.getDataKeySettingsForm)) { + result.dataKeySettingsForm = widgetTypeInstance.getDataKeySettingsForm(); } - if (isFunction(widgetTypeInstance.getDataKeySettingsSchema)) { - result.dataKeySettingsSchema = widgetTypeInstance.getDataKeySettingsSchema(); + if (isFunction(widgetTypeInstance.getLatestDataKeySettingsForm)) { + result.latestDataKeySettingsForm = widgetTypeInstance.getLatestDataKeySettingsForm(); } - if (isFunction(widgetTypeInstance.getLatestDataKeySettingsSchema)) { - result.latestDataKeySettingsSchema = widgetTypeInstance.getLatestDataKeySettingsSchema(); + + /** Start migrate from old JSON Schema Form **/ + + if (isFunction((widgetTypeInstance as any).getSettingsSchema) && !result.settingsForm?.length) { + const settingsSchema = (widgetTypeInstance as any).getSettingsSchema(); + result.settingsForm = jsonFormSchemaToFormProperties(settingsSchema); } + if (isFunction((widgetTypeInstance as any).getDataKeySettingsSchema) && !result.dataKeySettingsForm?.length) { + const dataKeySettingsSchema = (widgetTypeInstance as any).getDataKeySettingsSchema(); + result.dataKeySettingsForm = jsonFormSchemaToFormProperties(dataKeySettingsSchema); + } + if (isFunction((widgetTypeInstance as any).getLatestDataKeySettingsSchema) && !result.latestDataKeySettingsForm?.length) { + const latestDataKeySettingsSchema = (widgetTypeInstance as any).getLatestDataKeySettingsSchema(); + result.latestDataKeySettingsForm = jsonFormSchemaToFormProperties(latestDataKeySettingsSchema); + } + + /** End migrate from old JSON Schema Form **/ + if (isFunction(widgetTypeInstance.typeParameters)) { result.typeParameters = widgetTypeInstance.typeParameters(); } else { diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html index 3af9faca69..b2d1d68ed0 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html @@ -32,9 +32,6 @@
-
- -
widget-config.card-title
@@ -313,17 +310,5 @@
- -
- - -
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts index c9bbcab5d7..52fce38975 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts @@ -32,10 +32,8 @@ import { CellClickColumnInfo, DataKey, datasourcesHasAggregation, - datasourcesHasOnlyComparisonAggregation, DynamicFormData, - GroupInfo, - JsonSchema, - JsonSettingsSchema, + datasourcesHasOnlyComparisonAggregation, + DynamicFormData, TargetDevice, targetDeviceValid, Widget, @@ -74,7 +72,6 @@ import { import { catchError, map, mergeMap, tap } from 'rxjs/operators'; import { MatDialog } from '@angular/material/dialog'; import { EntityService } from '@core/http/entity.service'; -import { JsonFormComponentData } from '@shared/components/json-form/json-form-component.models'; import { Dashboard } from '@shared/models/dashboard.models'; import { entityFields } from '@shared/models/entity.models'; import { Filter, singleEntityFilterFromDeviceId } from '@shared/models/query/query.models'; @@ -84,17 +81,9 @@ import { coerceBoolean } from '@shared/decorators/coercion'; import { basicWidgetConfigComponentsMap } from '@home/components/widget/config/basic/basic-widget-config.module'; import { TimewindowConfigData } from '@home/components/widget/config/timewindow-config-panel.component'; import { DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models'; +import { defaultFormProperties, FormProperty } from '@shared/models/dynamic-form.models'; import Timeout = NodeJS.Timeout; -const emptySettingsSchema: JsonSchema = { - type: 'object', - properties: {} -}; -const emptySettingsGroupInfoes: GroupInfo[] = []; -const defaultSettingsForm = [ - '*' -]; - @Component({ selector: 'tb-widget-config', templateUrl: './widget-config.component.html', @@ -190,7 +179,6 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe public widgetSettings: UntypedFormGroup; public layoutSettings: UntypedFormGroup; public advancedSettings: UntypedFormGroup; - public oldAdvancedSettings: UntypedFormGroup; public actionsSettings: UntypedFormGroup; private createBasicModeComponentTimeout: Timeout; @@ -204,7 +192,6 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe private widgetSettingsSubscription: Subscription; private layoutSettingsSubscription: Subscription; private advancedSettingsSubscription: Subscription; - private oldAdvancedSettingsSubscription: Subscription; private actionsSettingsSubscription: Subscription; private defaultConfigFormsType: widgetType; @@ -223,7 +210,6 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe this.dataSettings = this.fb.group({}); this.targetDeviceSettings = this.fb.group({}); this.advancedSettings = this.fb.group({}); - this.oldAdvancedSettings = this.fb.group({}); this.widgetSettings = this.fb.group({ title: [null, []], titleFont: [null, []], @@ -299,10 +285,6 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe this.advancedSettingsSubscription.unsubscribe(); this.advancedSettingsSubscription = null; } - if (this.oldAdvancedSettingsSubscription) { - this.oldAdvancedSettingsSubscription.unsubscribe(); - this.oldAdvancedSettingsSubscription = null; - } if (this.actionsSettingsSubscription) { this.actionsSettingsSubscription.unsubscribe(); this.actionsSettingsSubscription = null; @@ -325,9 +307,6 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe this.advancedSettingsSubscription = this.advancedSettings.valueChanges.subscribe( () => this.updateAdvancedSettings() ); - this.oldAdvancedSettingsSubscription = this.oldAdvancedSettings.valueChanges.subscribe( - () => this.updateOldAdvancedSettings() - ); this.actionsSettingsSubscription = this.actionsSettings.valueChanges.subscribe( () => this.updateActionSettings() ); @@ -350,12 +329,6 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe value: 'appearance' } ); - this.headerOptions.push( - { - name: 'Old appearance', - value: 'oldAppearance' - } - ); } this.headerOptions.push( { @@ -409,8 +382,6 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe } this.advancedSettings.addControl('settings', this.fb.control(null, [])); - this.oldAdvancedSettings.addControl('settings', - this.fb.control(null, [])); } registerOnChange(fn: any): void { @@ -582,7 +553,6 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe } this.updateAdvancedForm(config.settings); - this.updateSchemaFormOld(config.settings); if (layout) { this.layoutSettings.patchValue( @@ -664,23 +634,6 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe this.advancedSettings.patchValue({ settings: dynamicFormData }, {emitEvent: false}); } - private updateSchemaFormOld(settings?: any) { - const widgetSettingsFormData: JsonFormComponentData = {}; - if (this.modelValue.settingsSchema && this.modelValue.settingsSchema.schema) { - widgetSettingsFormData.schema = this.modelValue.settingsSchema.schema; - widgetSettingsFormData.form = this.modelValue.settingsSchema.form || deepClone(defaultSettingsForm); - widgetSettingsFormData.groupInfoes = this.modelValue.settingsSchema.groupInfoes; - widgetSettingsFormData.model = settings; - } else { - widgetSettingsFormData.schema = deepClone(emptySettingsSchema); - widgetSettingsFormData.form = deepClone(defaultSettingsForm); - widgetSettingsFormData.groupInfoes = deepClone(emptySettingsGroupInfoes); - widgetSettingsFormData.model = settings || {}; - } - widgetSettingsFormData.settingsDirective = this.modelValue.settingsDirective; - this.oldAdvancedSettings.patchValue({ settings: widgetSettingsFormData }, {emitEvent: false}); - } - private updateDataSettings() { if (this.modelValue) { if (this.modelValue.config) { @@ -732,15 +685,6 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe } } - private updateOldAdvancedSettings() { - if (this.modelValue) { - if (this.modelValue.config) { - this.modelValue.config.settings = this.advancedSettings.get('settings').value?.model; - } - this.propagateChange(this.modelValue); - } - } - private updateActionSettings() { if (this.modelValue) { if (this.modelValue.config) { @@ -763,7 +707,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe } public get displayAdvancedAppearance(): boolean { - return !!this.modelValue && (!!this.modelValue.settingsSchema && !!this.modelValue.settingsSchema.schema || + return !!this.modelValue && (!!this.modelValue.settingsForm && !!this.modelValue.settingsForm.length || !!this.modelValue.settingsDirective && !!this.modelValue.settingsDirective.length); } @@ -802,7 +746,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe } } - public generateDataKey(chip: any, type: DataKeyType, datakeySettingsSchema: JsonSettingsSchema, + public generateDataKey(chip: any, type: DataKeyType, dataKeySettingsForm: FormProperty[], isLatestDataKey: boolean, dataKeySettingsFunction: DataKeySettingsFunction): DataKey { if (isObject(chip)) { (chip as DataKey)._hash = Math.random(); @@ -834,8 +778,8 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe } else if (type === DataKeyType.count) { result.name = 'count'; } - if (datakeySettingsSchema && isDefined(datakeySettingsSchema.schema)) { - result.settings = this.utils.generateObjectFromJsonSchema(datakeySettingsSchema.schema); + if (dataKeySettingsForm?.length) { + result.settings = defaultFormProperties(dataKeySettingsForm); } else if (dataKeySettingsFunction) { const settings = dataKeySettingsFunction(result, isLatestDataKey); if (settings) { diff --git a/ui-ngx/src/app/modules/home/models/widget-component.models.ts b/ui-ngx/src/app/modules/home/models/widget-component.models.ts index 8de7dbfb8b..b1958cb3db 100644 --- a/ui-ngx/src/app/modules/home/models/widget-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/widget-component.models.ts @@ -21,7 +21,6 @@ import { DatasourceData, FormattedData, fullWidgetTypeFqn, - JsonSettingsSchema, Widget, WidgetActionDescriptor, WidgetActionSource, @@ -562,9 +561,8 @@ export interface WidgetInfo extends WidgetTypeDescriptor, WidgetControllerDescri deprecated: boolean; scada: boolean; typeSettingsForm?: FormProperty[]; - typeSettingsSchema?: string | any; - typeDataKeySettingsSchema?: string | any; - typeLatestDataKeySettingsSchema?: string | any; + typeDataKeySettingsForm?: FormProperty[]; + typeLatestDataKeySettingsForm?: FormProperty[]; image?: string; description?: string; tags?: string[]; @@ -580,9 +578,8 @@ export interface WidgetConfigComponentData { actionSources: {[actionSourceId: string]: WidgetActionSource}; isDataEnabled: boolean; settingsForm: FormProperty[]; - settingsSchema: JsonSettingsSchema; - dataKeySettingsSchema: JsonSettingsSchema; - latestDataKeySettingsSchema: JsonSettingsSchema; + dataKeySettingsForm: FormProperty[]; + latestDataKeySettingsForm: FormProperty[]; dataKeySettingsFunction: DataKeySettingsFunction; settingsDirective: string; dataKeySettingsDirective: string; @@ -605,8 +602,8 @@ export const MissingWidgetType: WidgetInfo = { '
', templateCss: '', controllerScript: 'self.onInit = function() {}', - settingsSchema: '{}\n', - dataKeySettingsSchema: '{}\n', + settingsForm: [], + dataKeySettingsForm: [], image: null, description: null, defaultConfig: '{\n' + @@ -632,8 +629,8 @@ export const ErrorWidgetType: WidgetInfo = { '
', templateCss: '', controllerScript: 'self.onInit = function() {}', - settingsSchema: '{}\n', - dataKeySettingsSchema: '{}\n', + settingsForm: [], + dataKeySettingsForm: [], image: null, description: null, defaultConfig: '{\n' + @@ -646,9 +643,8 @@ export const ErrorWidgetType: WidgetInfo = { export interface WidgetTypeInstance { getSettingsForm?: () => FormProperty[]; - getSettingsSchema?: () => string; - getDataKeySettingsSchema?: () => string; - getLatestDataKeySettingsSchema?: () => string; + getDataKeySettingsForm?: () => FormProperty[]; + getLatestDataKeySettingsForm?: () => FormProperty[]; typeParameters?: () => WidgetTypeParameters; useCustomDatasources?: () => boolean; actionSources?: () => {[actionSourceId: string]: WidgetActionSource}; @@ -675,9 +671,8 @@ export const toWidgetInfo = (widgetTypeEntity: WidgetType): WidgetInfo => ({ templateCss: widgetTypeEntity.descriptor.templateCss, controllerScript: widgetTypeEntity.descriptor.controllerScript, settingsForm: widgetTypeEntity.descriptor.settingsForm, - settingsSchema: widgetTypeEntity.descriptor.settingsSchema, - dataKeySettingsSchema: widgetTypeEntity.descriptor.dataKeySettingsSchema, - latestDataKeySettingsSchema: widgetTypeEntity.descriptor.latestDataKeySettingsSchema, + dataKeySettingsForm: widgetTypeEntity.descriptor.dataKeySettingsForm, + latestDataKeySettingsForm: widgetTypeEntity.descriptor.latestDataKeySettingsForm, settingsDirective: widgetTypeEntity.descriptor.settingsDirective, dataKeySettingsDirective: widgetTypeEntity.descriptor.dataKeySettingsDirective, latestDataKeySettingsDirective: widgetTypeEntity.descriptor.latestDataKeySettingsDirective, @@ -705,9 +700,8 @@ export const toWidgetType = (widgetInfo: WidgetInfo, id: WidgetTypeId, tenantId: templateCss: widgetInfo.templateCss, controllerScript: widgetInfo.controllerScript, settingsForm: widgetInfo.settingsForm, - settingsSchema: widgetInfo.settingsSchema, - dataKeySettingsSchema: widgetInfo.dataKeySettingsSchema, - latestDataKeySettingsSchema: widgetInfo.latestDataKeySettingsSchema, + dataKeySettingsForm: widgetInfo.dataKeySettingsForm, + latestDataKeySettingsForm: widgetInfo.latestDataKeySettingsForm, settingsDirective: widgetInfo.settingsDirective, dataKeySettingsDirective: widgetInfo.dataKeySettingsDirective, latestDataKeySettingsDirective: widgetInfo.latestDataKeySettingsDirective, diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html index 02f0ecead8..5e550cdc69 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html @@ -199,52 +199,26 @@
- -
-
- - -
-
-
-
- -
-
- - -
-
+ +
+ +
- -
-
- - -
-
+ +
+ +
diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.ts b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.ts index 0a3d711dee..71b30c1464 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.ts +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.ts @@ -32,6 +32,7 @@ import { AppState } from '@core/core.state'; import { WidgetService } from '@core/http/widget.service'; import { detailsToWidgetInfo, WidgetInfo } from '@home/models/widget-component.models'; import { + migrateWidgetTypeToDynamicForms, TargetDeviceType, Widget, WidgetConfig, @@ -71,7 +72,7 @@ import { loadModulesCompleter } from '@shared/models/js-function.models'; import { TbPopoverService } from '@shared/components/popover.service'; import { JsFuncModulesComponent } from '@shared/components/js-func-modules.component'; import { MatIconButton } from '@angular/material/button'; -import { formPropertyCompletions, jsonFormSchemaToFormProperties } from '@shared/models/dynamic-form.models'; +import { formPropertyCompletions } from '@shared/models/dynamic-form.models'; import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe'; import Timeout = NodeJS.Timeout; @@ -108,15 +109,6 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe @ViewChild('cssInput', {static: true}) cssInputElmRef: ElementRef; - @ViewChild('settingsJsonInput', {static: true}) - settingsJsonInputElmRef: ElementRef; - - @ViewChild('dataKeySettingsJsonInput', {static: true}) - dataKeySettingsJsonInputElmRef: ElementRef; - - @ViewChild('latestDataKeySettingsJsonInput', {static: true}) - latestDataKeySettingsJsonInputElmRef: ElementRef; - @ViewChild('javascriptInput', {static: true}) javascriptInputElmRef: ElementRef; @@ -154,9 +146,6 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe fullscreen = false; htmlFullscreen = false; cssFullscreen = false; - jsonSettingsFullscreen = false; - jsonDataKeySettingsFullscreen = false; - jsonLatestDataKeySettingsFullscreen = false; javascriptFullscreen = false; iFrameFullscreen = false; @@ -164,9 +153,6 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe editorsResizeCafs: {[editorId: string]: CancelAnimationFrame} = {}; htmlEditor: Ace.Editor; cssEditor: Ace.Editor; - jsonSettingsEditor: Ace.Editor; - dataKeyJsonSettingsEditor: Ace.Editor; - latestDataKeyJsonSettingsEditor: Ace.Editor; jsEditor: Ace.Editor; private initialCompleters: Ace.Completer[]; aceResize$: ResizeObserver; @@ -227,9 +213,6 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe const config = JSON.parse(this.widget.defaultConfig); this.widget.defaultConfig = JSON.stringify(config); } - if (!this.widget.settingsForm?.length) { - this.widget.settingsForm = jsonFormSchemaToFormProperties(this.widget.settingsSchema); - } this.origWidget = deepClone(this.widget); if (!this.widgetTypeDetails) { this.isDirty = true; @@ -360,45 +343,6 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe }) )); - editorsObservables.push(this.createAceEditor(this.settingsJsonInputElmRef, 'json').pipe( - tap((editor) => { - this.jsonSettingsEditor = editor; - this.jsonSettingsEditor.on('input', () => { - const editorValue = this.jsonSettingsEditor.getValue(); - if (this.widget.settingsSchema !== editorValue) { - this.widget.settingsSchema = editorValue; - this.isDirty = true; - } - }); - }) - )); - - editorsObservables.push(this.createAceEditor(this.dataKeySettingsJsonInputElmRef, 'json').pipe( - tap((editor) => { - this.dataKeyJsonSettingsEditor = editor; - this.dataKeyJsonSettingsEditor.on('input', () => { - const editorValue = this.dataKeyJsonSettingsEditor.getValue(); - if (this.widget.dataKeySettingsSchema !== editorValue) { - this.widget.dataKeySettingsSchema = editorValue; - this.isDirty = true; - } - }); - }) - )); - - editorsObservables.push(this.createAceEditor(this.latestDataKeySettingsJsonInputElmRef, 'json').pipe( - tap((editor) => { - this.latestDataKeyJsonSettingsEditor = editor; - this.latestDataKeyJsonSettingsEditor.on('input', () => { - const editorValue = this.latestDataKeyJsonSettingsEditor.getValue(); - if (this.widget.latestDataKeySettingsSchema !== editorValue) { - this.widget.latestDataKeySettingsSchema = editorValue; - this.isDirty = true; - } - }); - }) - )); - editorsObservables.push(this.createAceEditor(this.javascriptInputElmRef, 'javascript').pipe( tap((editor) => { this.jsEditor = editor; @@ -432,10 +376,6 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe private setAceEditorValues() { this.htmlEditor.setValue(this.widget.templateHtml ? this.widget.templateHtml : '', -1); this.cssEditor.setValue(this.widget.templateCss ? this.widget.templateCss : '', -1); - this.jsonSettingsEditor.setValue(this.widget.settingsSchema ? this.widget.settingsSchema : '', -1); - this.dataKeyJsonSettingsEditor.setValue(this.widget.dataKeySettingsSchema ? this.widget.dataKeySettingsSchema : '', -1); - this.latestDataKeyJsonSettingsEditor.setValue(this.widget.latestDataKeySettingsSchema ? - this.widget.latestDataKeySettingsSchema : '', -1); this.jsEditor.setValue(this.controllerScriptBody ? this.controllerScriptBody : '', -1); this.updateControllerScriptCompleters(); } @@ -608,7 +548,11 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe }), catchError((err) => { if (id && err.status === HttpStatusCode.Conflict) { - return this.widgetService.getWidgetTypeById(id.id); + return this.widgetService.getWidgetTypeById(id.id).pipe( + map((details) => { + return migrateWidgetTypeToDynamicForms(details); + }) + ); } return throwError(() => err); }), @@ -766,43 +710,6 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe ); } - beautifyJson(): void { - beautifyJs(this.widget.settingsSchema, {indent_size: 4}).subscribe( - (res) => { - if (this.widget.settingsSchema !== res) { - this.isDirty = true; - this.widget.settingsSchema = res; - this.jsonSettingsEditor.setValue(this.widget.settingsSchema ? this.widget.settingsSchema : '', -1); - } - } - ); - } - - beautifyDataKeyJson(): void { - beautifyJs(this.widget.dataKeySettingsSchema, {indent_size: 4}).subscribe( - (res) => { - if (this.widget.dataKeySettingsSchema !== res) { - this.isDirty = true; - this.widget.dataKeySettingsSchema = res; - this.dataKeyJsonSettingsEditor.setValue(this.widget.dataKeySettingsSchema ? this.widget.dataKeySettingsSchema : '', -1); - } - } - ); - } - - beautifyLatestDataKeyJson(): void { - beautifyJs(this.widget.latestDataKeySettingsSchema, {indent_size: 4}).subscribe( - (res) => { - if (this.widget.latestDataKeySettingsSchema !== res) { - this.isDirty = true; - this.widget.latestDataKeySettingsSchema = res; - this.latestDataKeyJsonSettingsEditor.setValue(this.widget.latestDataKeySettingsSchema ? - this.widget.latestDataKeySettingsSchema : '', -1); - } - } - ); - } - beautifyJs(): void { beautifyJs(this.controllerScriptBody, {indent_size: 4, wrap_line_length: 60}).subscribe( (res) => { @@ -890,6 +797,14 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe this.updateControllerScriptCompleters(); } + dataKeySettingsFormUpdated() { + this.isDirty = true; + } + + latestDataKeySettingsFormUpdated() { + this.isDirty = true; + } + editControllerScriptModules($event: Event, button: MatIconButton) { if ($event) { $event.stopPropagation(); diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-library-routing.module.ts b/ui-ngx/src/app/modules/home/pages/widget/widget-library-routing.module.ts index 046737ac10..acd48f79ab 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widget-library-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-library-routing.module.ts @@ -26,7 +26,12 @@ import { WidgetService } from '@core/http/widget.service'; import { WidgetEditorComponent } from '@home/pages/widget/widget-editor.component'; import { map } from 'rxjs/operators'; import { detailsToWidgetInfo, WidgetInfo } from '@home/models/widget-component.models'; -import { widgetType, WidgetTypeDetails, WidgetTypeInfo } from '@app/shared/models/widget.models'; +import { + migrateWidgetTypeToDynamicForms, + widgetType, + WidgetTypeDetails, + WidgetTypeInfo +} from '@app/shared/models/widget.models'; import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; import { RouterTabsComponent } from '@home/components/router-tabs.component'; import { WidgetTypesTableConfigResolver } from '@home/pages/widget/widget-types-table-config.resolver'; @@ -68,10 +73,13 @@ const widgetEditorDataResolver: ResolveFn = (route: ActivatedR ); } else { return inject(WidgetService).getWidgetTypeById(widgetTypeId).pipe( - map((result) => ({ - widgetTypeDetails: result, - widget: detailsToWidgetInfo(result) - })) + map((result) => { + result = migrateWidgetTypeToDynamicForms(result); + return { + widgetTypeDetails: result, + widget: detailsToWidgetInfo(result) + }; + }) ); } }; diff --git a/ui-ngx/src/app/shared/components/json-form/json-form-component.models.ts b/ui-ngx/src/app/shared/components/json-form/json-form-component.models.ts deleted file mode 100644 index 75bcf2d1fd..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/json-form-component.models.ts +++ /dev/null @@ -1,23 +0,0 @@ -/// -/// 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 { JsonSettingsSchema } from '@shared/models/widget.models'; - -export interface JsonFormComponentData extends JsonSettingsSchema { - model?: any; - settingsDirective?: string; -} diff --git a/ui-ngx/src/app/shared/components/json-form/json-form.component.html b/ui-ngx/src/app/shared/components/json-form/json-form.component.html deleted file mode 100644 index 0ed848ded5..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/json-form.component.html +++ /dev/null @@ -1,23 +0,0 @@ - -
-
-
-
diff --git a/ui-ngx/src/app/shared/components/json-form/json-form.component.scss b/ui-ngx/src/app/shared/components/json-form/json-form.component.scss deleted file mode 100644 index 3ab3d5f26b..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/json-form.component.scss +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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. - */ -.tb-json-form { - padding: 12px; - padding-bottom: 14px !important; - overflow: auto; -} diff --git a/ui-ngx/src/app/shared/components/json-form/json-form.component.ts b/ui-ngx/src/app/shared/components/json-form/json-form.component.ts deleted file mode 100644 index 76647b5bae..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/json-form.component.ts +++ /dev/null @@ -1,293 +0,0 @@ -/// -/// 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 { - ChangeDetectorRef, - Component, - ElementRef, - forwardRef, - Input, - OnChanges, - OnDestroy, - Renderer2, - SimpleChanges, - ViewChild, ViewContainerRef, - ViewEncapsulation -} from '@angular/core'; -import { ControlValueAccessor, UntypedFormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, Validator } from '@angular/forms'; -import { coerceBooleanProperty } from '@angular/cdk/coercion'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { deepClone, isString, unwrapModule } from '@app/core/utils'; -import { JsonFormProps } from './react/json-form.models'; -import inspector from 'schema-inspector'; -import tinycolor from 'tinycolor2'; -import { DialogService } from '@app/core/services/dialog.service'; -import JsonFormUtils from './react/json-form-utils'; -import { JsonFormComponentData } from './json-form-component.models'; -import { GroupInfo } from '@shared/models/widget.models'; -import { Observable } from 'rxjs/internal/Observable'; -import { forkJoin, from } from 'rxjs'; -import { TbPopoverService } from '@shared/components/popover.service'; - -@Component({ - selector: 'tb-json-form', - templateUrl: './json-form.component.html', - styleUrls: ['./json-form.component.scss'], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => JsonFormComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => JsonFormComponent), - multi: true, - } - ], - encapsulation: ViewEncapsulation.None -}) -export class JsonFormComponent implements ControlValueAccessor, Validator, OnChanges, OnDestroy { - - @ViewChild('reactRoot', {static: true}) - reactRootElmRef: ElementRef; - - @ViewChild('reactFullscreen', {static: true}) - reactFullscreenElmRef: ElementRef; - - private readonlyValue: boolean; - get readonly(): boolean { - return this.readonlyValue; - } - @Input() - set required(value: boolean) { - this.readonlyValue = coerceBooleanProperty(value); - } - - formProps: JsonFormProps = { - isFullscreen: false, - option: { - formDefaults: { - startEmpty: true - } - }, - onModelChange: this.onModelChange.bind(this), - onColorClick: this.onColorClick.bind(this), - onIconClick: this.onIconClick.bind(this), - onToggleFullscreen: this.onToggleFullscreen.bind(this), - onHelpClick: this.onHelpClick.bind(this) - }; - - data: JsonFormComponentData; - - model: any; - schema: any; - form: any; - groupInfoes: GroupInfo[]; - - isModelValid = true; - - isFullscreen = false; - fullscreenFinishFn: (el: Element) => void; - - private reactRoot: any; - - private propagateChange = null; - private propagateChangePending = false; - private writingValue = false; - private updateViewPending = false; - - constructor(public elementRef: ElementRef, - private dialogs: DialogService, - private popoverService: TbPopoverService, - private renderer: Renderer2, - private viewContainerRef: ViewContainerRef, - protected store: Store, - private cd: ChangeDetectorRef) { - } - - ngOnDestroy(): void { - this.destroyReactSchemaForm(); - } - - registerOnChange(fn: any): void { - this.propagateChange = fn; - if (this.propagateChangePending) { - this.propagateChangePending = false; - setTimeout(() => { - this.propagateChange(this.data); - }, 0); - } - } - - registerOnTouched(fn: any): void { - } - - setDisabledState(isDisabled: boolean): void { - } - - public validate(c: UntypedFormControl) { - return this.isModelValid ? null : { - modelValid: false - }; - } - - writeValue(data: JsonFormComponentData): void { - this.writingValue = true; - this.data = data; - this.schema = this.data && this.data.schema ? deepClone(this.data.schema) : { - type: 'object' - }; - this.schema.strict = true; - this.form = this.data && this.data.form ? deepClone(this.data.form) : [ '*' ]; - this.groupInfoes = this.data && this.data.groupInfoes ? deepClone(this.data.groupInfoes) : []; - this.model = this.data && this.data.model || {}; - this.model = inspector.sanitize(this.schema, this.model).data; - this.updateAndRender(); - this.isModelValid = this.validateModel(); - this.writingValue = false; - if (!this.isModelValid || this.updateViewPending) { - this.updateView(); - } -} - - updateView() { - if (!this.writingValue) { - this.updateViewPending = false; - if (this.data) { - this.data.model = this.model; - if (this.propagateChange) { - try { - this.propagateChange(this.data); - } catch (e) { - this.propagateChangePending = true; - } - } else { - this.propagateChangePending = true; - } - } - } else { - this.updateViewPending = true; - } - } - - ngOnChanges(changes: SimpleChanges): void { - for (const propName of Object.keys(changes)) { - const change = changes[propName]; - if (!change.firstChange && change.currentValue !== change.previousValue) { - if (propName === 'readonly') { - this.updateAndRender(); - } - } - } - } - - private onModelChange(key: (string | number)[], val: any, forceUpdate = false) { - if (isString(val) && val === '') { - val = undefined; - } - if (JsonFormUtils.updateValue(key, this.model, val) || forceUpdate) { - this.isModelValid = this.validateModel(); - this.updateView(); - } - } - - private onColorClick(key: (string | number)[], - val: tinycolor.ColorFormats.RGBA, - colorSelectedFn: (color: tinycolor.ColorFormats.RGBA) => void) { - this.dialogs.colorPicker(tinycolor(val).toRgbString()).subscribe((result) => { - if (!result?.canceled && colorSelectedFn) { - colorSelectedFn(tinycolor(result?.color).toRgb()); - } - }); - } - - private onIconClick(key: (string | number)[], - val: string, - iconSelectedFn: (icon: string) => void) { - this.dialogs.materialIconPicker(val).subscribe((result) => { - if (!result?.canceled && iconSelectedFn) { - iconSelectedFn(result?.icon); - } - }); - } - - private onToggleFullscreen(fullscreenFinishFn?: (el: Element) => void) { - this.isFullscreen = !this.isFullscreen; - this.fullscreenFinishFn = fullscreenFinishFn; - this.cd.markForCheck(); - } - - onFullscreenChanged(fullscreen: boolean) { - this.formProps.isFullscreen = fullscreen; - this.renderReactSchemaForm(false); - if (this.fullscreenFinishFn) { - this.fullscreenFinishFn(this.reactFullscreenElmRef.nativeElement); - this.fullscreenFinishFn = null; - } - } - - private onHelpClick(event: MouseEvent, helpId: string, helpVisibleFn: (visible: boolean) => void, helpReadyFn: (ready: boolean) => void) { - const trigger = event.currentTarget as Element; - this.popoverService.toggleHelpPopover(trigger, this.renderer, this.viewContainerRef, helpId, '', '', null, helpVisibleFn, helpReadyFn); - } - - private updateAndRender() { - - this.formProps.option.formDefaults.readonly = this.readonly; - this.formProps.schema = this.schema; - this.formProps.form = this.form; - this.formProps.groupInfoes = this.groupInfoes; - this.formProps.model = this.model; - this.renderReactSchemaForm(); - } - - private renderReactSchemaForm(destroy: boolean = true) { - if (destroy) { - this.destroyReactSchemaForm(); - } - - // import ReactSchemaForm from './react/json-form-react'; - const reactSchemaFormObservables: Observable[] = [ - from(import('react')), - from(import('react-dom')), - from(import('react-dom/client')), - from(import('./react/json-form-react')) - ]; - forkJoin(reactSchemaFormObservables).subscribe( - (modules) => { - const react = unwrapModule(modules[0]); - const reactDomClient = unwrapModule(modules[2]); - const jsonFormReact = unwrapModule(modules[3]); - this.reactRoot = reactDomClient.createRoot(this.reactRootElmRef.nativeElement); - this.reactRoot.render(react.createElement(jsonFormReact, this.formProps)); - } - ); - } - - private destroyReactSchemaForm() { - this.reactRoot?.unmount(); - } - - private validateModel(): boolean { - if (this.schema && this.model) { - return JsonFormUtils.validateBySchema(this.schema, this.model).valid; - } - return true; - } -} - diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-ace-editor.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-ace-editor.tsx deleted file mode 100644 index 649ee16c95..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-ace-editor.tsx +++ /dev/null @@ -1,239 +0,0 @@ -/* - * 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 * as React from 'react'; -import * as ReactDOM from 'react-dom'; -import ThingsboardBaseComponent from './json-form-base-component'; -import reactCSS from 'reactcss'; -import Button from '@mui/material/Button'; -import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; -import { IEditorProps } from 'react-ace/src/types'; -import { map, mergeMap } from 'rxjs/operators'; -import { getAce } from '@shared/models/ace/ace.models'; -import { from, lastValueFrom } from 'rxjs'; -import { Observable } from 'rxjs/internal/Observable'; -import { CircularProgress, IconButton } from '@mui/material'; -import { MouseEvent } from 'react'; -import { Help, HelpOutline } from '@mui/icons-material'; -import { unwrapModule } from '@core/utils'; - -const ReactAce = React.lazy(() => { - return lastValueFrom(getAce().pipe( - mergeMap(() => { - return from(import('react-ace')).pipe( - map((module) => unwrapModule(module) - )); - }) - )); -}); - -interface ThingsboardAceEditorProps extends JsonFormFieldProps { - mode: string; - onTidy: (value: string) => Observable; -} - -interface ThingsboardAceEditorState extends JsonFormFieldState { - isFull: boolean; - fullscreenContainerElement: Element; - helpVisible: boolean; - helpReady: boolean; - focused: boolean; -} - -class ThingsboardAceEditor extends React.Component { - - private aceEditor: IEditorProps; - - constructor(props: ThingsboardAceEditorProps) { - super(props); - this.onValueChanged = this.onValueChanged.bind(this); - this.onBlur = this.onBlur.bind(this); - this.onFocus = this.onFocus.bind(this); - this.onTidy = this.onTidy.bind(this); - this.onHelp = this.onHelp.bind(this); - this.onLoad = this.onLoad.bind(this); - this.onToggleFull = this.onToggleFull.bind(this); - const value = props.value ? props.value + '' : ''; - this.state = { - isFull: false, - fullscreenContainerElement: null, - helpVisible: false, - helpReady: true, - value, - focused: false - }; - } - - onValueChanged(value) { - this.setState({ - value - }); - this.props.onChangeValidate({ - target: { - value - } - }); - } - - onBlur() { - this.setState({ focused: false }); - } - - onFocus() { - this.setState({ focused: true }); - } - - onTidy() { - if (!this.props.form.readonly) { - const value = this.state.value; - this.props.onTidy(value).subscribe( - (processedValue) => { - this.setState({ - value: processedValue - }); - this.props.onChangeValidate({ - target: { - value: processedValue - } - }); - } - ); - } - } - - onHelp(event: MouseEvent) { - if (this.state.helpVisible && !this.state.helpReady) { - event.preventDefault(); - event.stopPropagation(); - } else { - this.props.onHelpClick(event, this.props.form.helpId, - (visible) => { - this.setState({ - helpVisible: visible - }); - }, (ready) => { - this.setState({ - helpReady: ready - }); - }); - } - } - - onLoad(editor: IEditorProps) { - this.aceEditor = editor; - } - - onToggleFull() { - this.props.onToggleFullscreen((el) => { - this.setState({ isFull: !this.state.isFull, fullscreenContainerElement: el }); - }); - } - - componentDidUpdate() { - } - - render() { - - const styles = reactCSS({ - default: { - tidyButtonStyle: { - color: '#7B7B7B', - minWidth: '32px', - minHeight: '15px', - lineHeight: '15px', - fontSize: '0.800rem', - margin: '0', - padding: '4px', - height: '23px', - borderRadius: '5px', - marginLeft: '5px' - } - } - }); - - let labelClass = 'tb-label'; - if (this.props.form.required) { - labelClass += ' tb-required'; - } - if (this.props.form.readonly) { - labelClass += ' tb-readonly'; - } - if (this.state.focused) { - labelClass += ' tb-focused'; - } - let containerClass = 'tb-container'; - const style = this.props.form.style || {width: '100%'}; - if (this.state.isFull) { - containerClass += ' fullscreen-form-field'; - } - const formDom = ( -
- -
-
- - { this.props.onTidy ? : null } - { this.props.form.helpId ?
- - {this.state.helpVisible ? : } - - { this.state.helpVisible && !this.state.helpReady ? -
- -
: null }
: null } - -
- Loading...
}> - - -
-
{this.props.error}
-
- ); - if (this.state.isFull) { - return ReactDOM.createPortal(formDom, this.state.fullscreenContainerElement); - } else { - return ( -
- {formDom} -
- ); - } - } -} - -export default ThingsboardBaseComponent(ThingsboardAceEditor); diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-array.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-array.tsx deleted file mode 100644 index 681fe69aab..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-array.tsx +++ /dev/null @@ -1,177 +0,0 @@ -/* - * 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 * as React from 'react'; -import JsonFormUtils from './json-form-utils'; -import ThingsboardBaseComponent from './json-form-base-component'; -import Button from '@mui/material/Button'; -import _ from 'lodash'; -import IconButton from '@mui/material/IconButton'; -import Clear from '@mui/icons-material/Clear'; -import Add from '@mui/icons-material/Add'; -import Tooltip from '@mui/material/Tooltip'; -import { - JsonFormData, - JsonFormFieldProps, - JsonFormFieldState -} from '@shared/components/json-form/react/json-form.models'; - -interface ThingsboardArrayState extends JsonFormFieldState { - model: any[]; - keys: number[]; -} - -class ThingsboardArray extends React.Component { - - constructor(props: JsonFormFieldProps) { - super(props); - this.onAppend = this.onAppend.bind(this); - this.onDelete = this.onDelete.bind(this); - const model = JsonFormUtils.selectOrSet(this.props.form.key, this.props.model) || []; - const keys: number[] = []; - for (let i = 0; i < model.length; i++) { - keys.push(i); - } - this.state = { - model, - keys - }; - } - - componentDidMount() { - if (this.props.form.startEmpty !== true && this.state.model.length === 0) { - this.onAppend(); - } - } - - onAppend() { - let empty; - if (this.props.form && this.props.form.schema && this.props.form.schema.items) { - const items = this.props.form.schema.items; - if (items.type && items.type.indexOf('object') !== -1) { - empty = {}; - if (!this.props.options || this.props.options.setSchemaDefaults !== false) { - empty = typeof items.default !== 'undefined' ? items.default : empty; - if (empty) { - JsonFormUtils.traverseSchema(items, (prop, path) => { - if (typeof prop.default !== 'undefined') { - JsonFormUtils.selectOrSet(path, empty, prop.default); - } - }); - } - } - } else if (items.type && items.type.indexOf('array') !== -1) { - empty = []; - if (!this.props.options || this.props.options.setSchemaDefaults !== false) { - empty = items.default || empty; - } - } else { - if (!this.props.options || this.props.options.setSchemaDefaults !== false) { - empty = items.default || empty; - } - } - } - const newModel = this.state.model; - newModel.push(empty); - const newKeys = this.state.keys; - let key = 0; - if (newKeys.length > 0) { - key = newKeys[newKeys.length - 1] + 1; - } - newKeys.push(key); - this.setState({ - model: newModel, - keys: newKeys - } - ); - this.props.onChangeValidate(this.state.model, true); - } - - onDelete(index: number) { - const newModel = this.state.model; - newModel.splice(index, 1); - const newKeys = this.state.keys; - newKeys.splice(index, 1); - this.setState( - { - model: newModel, - keys: newKeys - } - ); - this.props.onChangeValidate(this.state.model, true); - } - - setIndex(index: number) { - return (form: JsonFormData) => { - if (form.key) { - form.key[form.key.indexOf('')] = index; - } - }; - } - - copyWithIndex(form: JsonFormData, index: number): JsonFormData { - const copy: JsonFormData = _.cloneDeep(form); - copy.arrayIndex = index; - JsonFormUtils.traverseForm(copy, this.setIndex(index)); - return copy; - } - - render() { - const arrays = []; - const model = this.state.model; - const keys = this.state.keys; - for (let i = 0; i < model.length; i++ ) { - let removeButton: React.JSX.Element = null; - if (!this.props.form.readonly) { - const boundOnDelete = this.onDelete.bind(this, i); - removeButton = ; - } - const forms = (this.props.form.items as JsonFormData[]).map((form, index) => { - const copy = this.copyWithIndex(form, i); - return this.props.builder(copy, this.props.model, index, this.props.onChange, - this.props.onColorClick, this.props.onIconClick, this.props.onToggleFullscreen, - this.props.onHelpClick, this.props.mapper); - }); - arrays.push( -
  • - {removeButton} - {forms} -
  • - ); - } - let addButton: JSX.Element = null; - if (!this.props.form.readonly) { - addButton = ; - } - - return ( -
    -
    -
    {this.props.form.title}
    -
      - {arrays} -
    -
    - {addButton} -
    - ); - } -} - -export default ThingsboardBaseComponent(ThingsboardArray); diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-base-component.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-base-component.tsx deleted file mode 100644 index 4e9687ce3a..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-base-component.tsx +++ /dev/null @@ -1,122 +0,0 @@ -/* - * 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 * as React from 'react'; -import JsonFormUtils from './json-form-utils'; -import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; -import { isDefinedAndNotNull } from '@core/utils'; - -export default ThingsboardBaseComponent => class

    - extends React.Component { - - constructor(props: P) { - super(props); - this.onChangeValidate = this.onChangeValidate.bind(this); - const value = this.defaultValue(); - const validationResult = JsonFormUtils.validate(this.props.form, value); - this.state = { - value, - valid: !!(validationResult.valid || !value), - error: !validationResult.valid && value ? validationResult.error.message : null - }; - } - - componentDidMount() { - if (typeof this.state.value !== 'undefined') { - this.props.onChange(this.props.form.key, this.state.value); - } - } - - onChangeValidate(e, forceUpdate?: boolean) { - let value = null; - if (this.props.form.schema.type === 'integer' || this.props.form.schema.type === 'number') { - if (!e || e.target?.value === null || e.target?.value === '') { - value = undefined; - } else if (typeof e === 'number') { - value = Number(e); - } else if (e.target.value.indexOf('.') === -1) { - value = parseInt(e.target.value, 10); - } else { - value = parseFloat(e.target.value); - } - } else if (this.props.form.schema.type === 'boolean') { - value = e.target.checked; - } else if (this.props.form.schema.type === 'date' || this.props.form.schema.type === 'array') { - value = e; - } else { // string - value = e.target.value; - } - const validationResult = JsonFormUtils.validate(this.props.form, value); - this.setState({ - value, - valid: validationResult.valid, - error: validationResult.valid ? null : validationResult.error.message - }); - this.props.onChange(this.props.form.key, value, forceUpdate); - } - - defaultValue() { - let value = JsonFormUtils.selectOrSet(this.props.form.key, this.props.model); - if (this.props.form.schema.type === 'boolean') { - if (typeof value !== 'boolean' && typeof this.props.form.default === 'boolean') { - value = this.props.form.default; - } - if (typeof value !== 'boolean' && this.props.form.schema && typeof this.props.form.schema.default === 'boolean') { - value = this.props.form.schema.default; - } - if (typeof value !== 'boolean' && - this.props.form.schema && - this.props.form.required) { - value = false; - } - } else if (this.props.form.schema.type === 'integer' || this.props.form.schema.type === 'number') { - if (typeof value !== 'number' && typeof this.props.form.default === 'number') { - value = this.props.form.default; - } - if (typeof value !== 'number' && this.props.form.schema && typeof this.props.form.schema.default === 'number') { - value = this.props.form.schema.default; - } - if (typeof value !== 'number' && this.props.form.titleMap && typeof this.props.form.titleMap[0].value === 'number') { - value = this.props.form.titleMap[0].value; - } - if (value && typeof value === 'string') { - if (value.indexOf('.') === -1) { - value = parseInt(value, 10); - } else { - value = parseFloat(value); - } - } - } else { - if (!value && isDefinedAndNotNull(this.props.form.default)) { - value = this.props.form.default; - } - if (!value && this.props.form.schema && isDefinedAndNotNull(this.props.form.schema.default)) { - value = this.props.form.schema.default; - } - if (!value && this.props.form.titleMap && isDefinedAndNotNull(this.props.form.titleMap[0].value)) { - value = this.props.form.titleMap[0].value; - } - } - return value; - } - - render() { - if (this.props.form && this.props.form.schema) { - return ; - } else { - return

    ; - } - } -}; diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-checkbox.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-checkbox.tsx deleted file mode 100644 index fd3bac8f2c..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-checkbox.tsx +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 * as React from 'react'; -import ThingsboardBaseComponent from './json-form-base-component'; -import Checkbox from '@mui/material/Checkbox'; -import { JsonFormFieldProps, JsonFormFieldState } from './json-form.models.js'; -import FormControlLabel from '@mui/material/FormControlLabel'; - -class ThingsboardCheckbox extends React.Component { - render() { - return ( -
    - { - this.props.onChangeValidate(e); - }} - /> - } - label={this.props.form.title} - /> -
    - ); - } -} - -export default ThingsboardBaseComponent(ThingsboardCheckbox); diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-color.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-color.tsx deleted file mode 100644 index bf3a245663..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-color.tsx +++ /dev/null @@ -1,186 +0,0 @@ -/* - * 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 * as React from 'react'; -import ThingsboardBaseComponent from './json-form-base-component'; -import reactCSS from 'reactcss'; -import tinycolor from 'tinycolor2'; -import TextField from '@mui/material/TextField'; -import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; -import IconButton from '@mui/material/IconButton'; -import Clear from '@mui/icons-material/Clear'; -import Tooltip from '@mui/material/Tooltip'; - -interface ThingsboardColorState extends JsonFormFieldState { - color: tinycolor.ColorFormats.RGBA | null; - focused: boolean; -} - -class ThingsboardColor extends React.Component { - - containerRef = React.createRef(); - - constructor(props: JsonFormFieldProps) { - super(props); - this.onBlur = this.onBlur.bind(this); - this.onFocus = this.onFocus.bind(this); - this.onValueChanged = this.onValueChanged.bind(this); - this.onSwatchClick = this.onSwatchClick.bind(this); - this.onClear = this.onClear.bind(this); - const value = props.value ? props.value + '' : null; - const color = value != null ? tinycolor(value).toRgb() : null; - this.state = { - color, - focused: false - }; - } - - onBlur() { - this.setState({focused: false}); - } - - onFocus() { - this.setState({focused: true}); - } - - componentDidMount() { - const node = this.containerRef.current; - const colContainer = $(node).children('#color-container'); - colContainer.click(() => { - if (!this.props.form.readonly) { - this.onSwatchClick(); - } - }); - } - - componentWillUnmount() { - const node = this.containerRef.current; - const colContainer = $(node).children('#color-container'); - colContainer.off( 'click' ); - } - - onValueChanged(value: tinycolor.ColorFormats.RGBA | null) { - let color: tinycolor.Instance = null; - if (value != null) { - color = tinycolor(value); - } - this.setState({ - color: value - }); - let colorValue = ''; - if (color != null && color.getAlpha() !== 1) { - colorValue = color.toRgbString(); - } else if (color != null) { - colorValue = color.toHexString(); - } - this.props.onChangeValidate({ - target: { - value: colorValue - } - }); - } - - onSwatchClick() { - this.props.onColorClick(this.props.form.key, this.state.color, - (color) => { - this.onValueChanged(color); - } - ); - } - - onClear(event: React.MouseEvent) { - if (event) { - event.stopPropagation(); - } - this.onValueChanged(null); - } - - render() { - - let background = 'rgba(0,0,0,0)'; - if (this.state.color != null) { - background = `rgba(${ this.state.color.r }, ${ this.state.color.g }, ${ this.state.color.b }, ${ this.state.color.a })`; - } - - const styles = reactCSS({ - default: { - color: { - background: `${ background }` - }, - swatch: { - display: 'inline-block', - marginRight: '10px', - marginTop: 'auto', - marginBottom: 'auto', - cursor: 'pointer', - opacity: `${ this.props.form.readonly ? '0.6' : '1' }` - }, - swatchText: { - width: '100%' - }, - container: { - display: 'flex', - flexDirection: 'row', - alignItems: 'center' - }, - colorContainer: { - display: 'flex', - width: '100%' - } - }, - }); - - let fieldClass = 'tb-field'; - if (this.props.form.required) { - fieldClass += ' tb-required'; - } - if (this.props.form.readonly) { - fieldClass += ' tb-readonly'; - } - if (this.state.focused) { - fieldClass += ' tb-focused'; - } - - let stringColor = ''; - if (this.state.color != null) { - const color = tinycolor(this.state.color); - stringColor = color.toRgbString(); - } - - return ( -
    -
    -
    -
    -
    - -
    - -
    - ); - } -} - -export default ThingsboardBaseComponent(ThingsboardColor); diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-css.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-css.tsx deleted file mode 100644 index 600ae8c12b..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-css.tsx +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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 * as React from 'react'; -import ThingsboardAceEditor from './json-form-ace-editor'; -import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; -import { Observable } from 'rxjs/internal/Observable'; -import { beautifyCss } from '@shared/models/beautify.models'; - -class ThingsboardCss extends React.Component { - - constructor(props: JsonFormFieldProps) { - super(props); - this.onTidyCss = this.onTidyCss.bind(this); - } - - onTidyCss(css: string): Observable { - return beautifyCss(css, {indent_size: 4}); - } - - render() { - return ( - - ); - } -} - -export default ThingsboardCss; diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-date.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-date.tsx deleted file mode 100644 index 3b253cfa8e..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-date.tsx +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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 * as React from 'react'; -import ThingsboardBaseComponent from './json-form-base-component'; -import { AdapterMoment } from '@mui/x-date-pickers/AdapterMoment' -import { LocalizationProvider, DatePicker } from '@mui/x-date-pickers'; -import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; -import moment from 'moment'; - -interface ThingsboardDateState extends JsonFormFieldState { - currentValue: Date | null; -} - -class ThingsboardDate extends React.Component { - - constructor(props: JsonFormFieldProps) { - super(props); - this.onDatePicked = this.onDatePicked.bind(this); - let value: Date | null = null; - if (this.props.value && typeof this.props.value === 'number') { - value = new Date(this.props.value); - } - this.state = { - currentValue: value - }; - } - - - onDatePicked(date: moment.Moment | null) { - this.setState({ - currentValue: date?.toDate() - }); - this.props.onChangeValidate(date ? date.valueOf() : null); - } - - render() { - - let fieldClass = 'tb-date-field'; - if (this.props.form.required) { - fieldClass += ' tb-required'; - } - if (this.props.form.readonly) { - fieldClass += ' tb-readonly'; - } - - return ( - -
    - - -
    -
    - ); - } -} - -export default ThingsboardBaseComponent(ThingsboardDate); diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-fieldset.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-fieldset.tsx deleted file mode 100644 index 31f09fc409..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-fieldset.tsx +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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 * as React from 'react'; -import { - JsonFormData, - JsonFormFieldProps, - JsonFormFieldState -} from '@shared/components/json-form/react/json-form.models'; - -class ThingsboardFieldSet extends React.Component { - - render() { - const forms = (this.props.form.items as JsonFormData[]).map((form: JsonFormData, index) => { - return this.props.builder(form, this.props.model, index, this.props.onChange, - this.props.onColorClick, this.props.onIconClick, this.props.onToggleFullscreen, this.props.onHelpClick, this.props.mapper); - }); - - return ( -
    -
    - {this.props.form.title} -
    -
    - {forms} -
    -
    - ); - } -} - -export default ThingsboardFieldSet; diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-help.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-help.tsx deleted file mode 100644 index 68536f457e..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-help.tsx +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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 * as React from 'react'; -import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; - -class ThingsboardHelp extends React.Component { - render() { - return ( -
    - ); - } -} - -export default ThingsboardHelp; diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-html.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-html.tsx deleted file mode 100644 index f267154182..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-html.tsx +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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 * as React from 'react'; -import ThingsboardAceEditor from './json-form-ace-editor'; -import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; -import { Observable } from 'rxjs/internal/Observable'; -import { beautifyHtml } from '@shared/models/beautify.models'; - -class ThingsboardHtml extends React.Component { - - constructor(props: JsonFormFieldProps) { - super(props); - this.onTidyHtml = this.onTidyHtml.bind(this); - } - - onTidyHtml(html: string): Observable { - return beautifyHtml(html, {indent_size: 4}); - } - - render() { - return ( - - ); - } -} - -export default ThingsboardHtml; diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-icon.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-icon.tsx deleted file mode 100644 index 00366b86e8..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-icon.tsx +++ /dev/null @@ -1,162 +0,0 @@ -/* - * 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 * as React from 'react'; -import { MouseEvent } from 'react'; -import ThingsboardBaseComponent from './json-form-base-component'; -import reactCSS from 'reactcss'; -import TextField from '@mui/material/TextField'; -import IconButton from '@mui/material/IconButton'; -import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; -import Clear from '@mui/icons-material/Clear'; -import Icon from '@mui/material/Icon'; -import Tooltip from '@mui/material/Tooltip'; - -interface ThingsboardIconState extends JsonFormFieldState { - icon: string | null; - focused: boolean; -} - -class ThingsboardIcon extends React.Component { - - containerRef = React.createRef(); - - constructor(props: JsonFormFieldProps) { - super(props); - this.onBlur = this.onBlur.bind(this); - this.onFocus = this.onFocus.bind(this); - this.onValueChanged = this.onValueChanged.bind(this); - this.onIconClick = this.onIconClick.bind(this); - this.onClear = this.onClear.bind(this); - const icon = props.value ? props.value : ''; - this.state = { - icon, - focused: false - }; - } - - onBlur() { - this.setState({focused: false}); - } - - onFocus() { - this.setState({focused: true}); - } - - componentDidMount() { - const node = this.containerRef.current; - const iconContainer = $(node).children('#icon-container'); - iconContainer.on('click', (event) => { - if (!this.props.form.readonly) { - this.onIconClick(event); - } - }); - } - - componentWillUnmount() { - const node = this.containerRef.current; - const iconContainer = $(node).children('#icon-container'); - iconContainer.off( 'click' ); - } - - onValueChanged(value: string | null) { - this.setState({ - icon: value - }); - this.props.onChange(this.props.form.key, value); - } - - onIconClick(_event) { - this.props.onIconClick(this.props.form.key, this.state.icon, - (color) => { - this.onValueChanged(color); - } - ); - } - - onClear(event: MouseEvent) { - if (event) { - event.stopPropagation(); - } - this.onValueChanged(''); - } - - render() { - - const styles = reactCSS({ - default: { - container: { - display: 'flex', - flexDirection: 'row', - alignItems: 'center' - }, - icon: { - padding: '12px', - marginRight: '10px', - marginBottom: 'auto', - cursor: 'pointer', - border: 'solid 1px rgba(0, 0, 0, .27)', - borderRadius: '0' - }, - iconContainer: { - display: 'flex', - width: '100%' - }, - iconText: { - width: '100%' - }, - }, - }); - - let fieldClass = 'tb-field'; - if (this.props.form.required) { - fieldClass += ' tb-required'; - } - if (this.state.focused) { - fieldClass += ' tb-focused'; - } - - let pickedIcon = 'more_horiz'; - let icon = ''; - if (this.state.icon !== '') { - pickedIcon = this.state.icon; - icon = this.state.icon; - } - - return ( -
    -
    - - {pickedIcon} - - -
    - -
    - ); - } -} - -export default ThingsboardBaseComponent(ThingsboardIcon); diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-image.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-image.tsx deleted file mode 100644 index 2a03d95669..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-image.tsx +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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 React, { MouseEvent } from 'react'; -import Dropzone from 'react-dropzone'; -import ThingsboardBaseComponent from './json-form-base-component'; -import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; -import IconButton from '@mui/material/IconButton'; -import Clear from '@mui/icons-material/Clear'; -import Tooltip from '@mui/material/Tooltip'; - -interface ThingsboardImageState extends JsonFormFieldState { - imageUrl: string; -} - -class ThingsboardImage extends React.Component { - - constructor(props: JsonFormFieldProps) { - super(props); - this.onDrop = this.onDrop.bind(this); - this.onClear = this.onClear.bind(this); - const value = props.value ? props.value + '' : null; - this.state = { - imageUrl: value - }; - } - - onDrop(acceptedFiles: File[]) { - const reader = new FileReader(); - reader.onload = () => { - this.onValueChanged(reader.result as string); - }; - reader.readAsDataURL(acceptedFiles[0]); - } - - onValueChanged(value: string) { - this.setState({ - imageUrl: value - }); - this.props.onChangeValidate({ - target: { - value - } - }); - } - - onClear(event: MouseEvent) { - if (event) { - event.stopPropagation(); - } - this.onValueChanged(''); - } - - render() { - - let labelClass = 'tb-label'; - if (this.props.form.required) { - labelClass += ' tb-required'; - } - if (this.props.form.readonly) { - labelClass += ' tb-readonly'; - } - - let previewComponent: React.JSX.Element; - if (this.state.imageUrl) { - previewComponent = ; - } else { - previewComponent =
    No image selected
    ; - } - - return ( -
    - -
    -
    {previewComponent}
    -
    - - - -
    - - {({getRootProps, getInputProps}) => ( -
    -
    Drop an image or click to select a file to upload.
    - -
    - )} -
    -
    -
    - ); - } -} - -export default ThingsboardBaseComponent(ThingsboardImage); diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-javascript.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-javascript.tsx deleted file mode 100644 index 74d01137a8..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-javascript.tsx +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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 * as React from 'react'; -import ThingsboardAceEditor from './json-form-ace-editor'; -import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; -import { Observable } from 'rxjs/internal/Observable'; -import { beautifyJs } from '@shared/models/beautify.models'; - -class ThingsboardJavaScript extends React.Component { - - constructor(props: JsonFormFieldProps) { - super(props); - this.onTidyJavascript = this.onTidyJavascript.bind(this); - } - - onTidyJavascript(javascript: string): Observable { - return beautifyJs(javascript, {indent_size: 4, wrap_line_length: 60}); - } - - render() { - return ( - - ); - } -} - -export default ThingsboardJavaScript; diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-json.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-json.tsx deleted file mode 100644 index 9ab839616b..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-json.tsx +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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 * as React from 'react'; -import ThingsboardAceEditor from './json-form-ace-editor'; -import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; -import { Observable } from 'rxjs/internal/Observable'; -import { beautifyJs } from '@shared/models/beautify.models'; - -class ThingsboardJson extends React.Component { - - constructor(props: JsonFormFieldProps) { - super(props); - this.onTidyJson = this.onTidyJson.bind(this); - } - - onTidyJson(json: string): Observable { - return beautifyJs(json, {indent_size: 4}); - } - - render() { - return ( - - ); - } -} - -export default ThingsboardJson; diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-markdown.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-markdown.tsx deleted file mode 100644 index 57fb97ae23..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-markdown.tsx +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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 * as React from 'react'; -import ThingsboardAceEditor from './json-form-ace-editor'; -import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; - -class ThingsboardMarkdown extends React.Component { - - constructor(props: JsonFormFieldProps) { - super(props); - } - - render() { - return ( - - ); - } -} - -export default ThingsboardMarkdown; diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-number.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-number.tsx deleted file mode 100644 index 4616324de2..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-number.tsx +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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 * as React from 'react'; -import ThingsboardBaseComponent from './json-form-base-component'; -import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; -import { TextField } from '@mui/material'; -import { ChangeEvent } from 'react'; - -interface ThingsboardNumberState extends JsonFormFieldState { - focused: boolean; - lastSuccessfulValue: number; -} - -class ThingsboardNumber extends React.Component { - - constructor(props: JsonFormFieldProps) { - super(props); - this.preValidationCheck = this.preValidationCheck.bind(this); - this.onBlur = this.onBlur.bind(this); - this.onFocus = this.onFocus.bind(this); - this.state = { - lastSuccessfulValue: this.props.value, - focused: false - }; - } - - isNumeric(n: any) { - return n === null || n === '' || !isNaN(n) && isFinite(n); - } - - onBlur() { - this.setState({focused: false}); - } - - onFocus() { - this.setState({focused: true}); - } - - preValidationCheck(e: ChangeEvent) { - if (this.isNumeric(e.target.value)) { - this.setState({ - lastSuccessfulValue: Number(e.target.value) - }); - this.props.onChangeValidate(e); - } - } - - render() { - - let fieldClass = 'tb-field'; - if (this.props.form.required) { - fieldClass += ' tb-required'; - } - if (this.props.form.readonly) { - fieldClass += ' tb-readonly'; - } - if (this.state.focused) { - fieldClass += ' tb-focused'; - } - let value = this.state.lastSuccessfulValue; - if (typeof value !== 'undefined') { - value = Number(value); - } else { - value = null; - } - return ( -
    - -
    - ); - } -} - -export default ThingsboardBaseComponent(ThingsboardNumber); diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-radios.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-radios.tsx deleted file mode 100644 index 99031ff480..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-radios.tsx +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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 * as React from 'react'; -import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; -import FormControlLabel from '@mui/material/FormControlLabel'; -import { FormLabel, Radio, RadioGroup } from '@mui/material'; -import FormControl from '@mui/material/FormControl'; -import ThingsboardBaseComponent from '@shared/components/json-form/react/json-form-base-component'; - -class ThingsboardRadios extends React.Component { - render() { - const items = this.props.form.titleMap.map((item, index) => { - return ( - } label={item.name} key={index} /> - ); - }); - - let row = false; - if (this.props.form.direction === 'row') { - row = true; - } - - return ( - - {this.props.form.title} - { - this.props.onChangeValidate(e); - }}> - {items} - - - ); - } -} - -export default ThingsboardBaseComponent(ThingsboardRadios); diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-rc-select.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-rc-select.tsx deleted file mode 100644 index ee21674b94..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-rc-select.tsx +++ /dev/null @@ -1,202 +0,0 @@ -/* - * 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 * as React from 'react'; -import ThingsboardBaseComponent from './json-form-base-component'; -import Select, { Option } from 'rc-select'; -import { - JsonFormFieldProps, - JsonFormFieldState, - KeyLabelItem -} from '@shared/components/json-form/react/json-form.models'; -import { Mode } from 'rc-select/lib/interface'; -import { deepClone } from '@core/utils'; - -interface ThingsboardRcSelectState extends JsonFormFieldState { - currentValue: KeyLabelItem | KeyLabelItem[]; - items: Array; - focused: boolean; -} - -class ThingsboardRcSelect extends React.Component { - - constructor(props: JsonFormFieldProps) { - super(props); - this.onSelect = this.onSelect.bind(this); - this.onDeselect = this.onDeselect.bind(this); - this.onBlur = this.onBlur.bind(this); - this.onFocus = this.onFocus.bind(this); - this.state = { - currentValue: this.keyToCurrentValue(this.props.value, this.props.form.schema.type === 'array'), - items: this.props.form.items as KeyLabelItem[], - focused: false - }; - } - - keyToCurrentValue(key: string | string[], isArray: boolean): KeyLabelItem | KeyLabelItem[] { - let currentValue: KeyLabelItem | KeyLabelItem[] = isArray ? [] : null; - if (isArray) { - const keys = key; - if (keys) { - (keys as string[]).forEach((keyVal) => { - (currentValue as KeyLabelItem[]).push({key: keyVal, label: this.labelFromKey(keyVal)}); - }); - } - } else { - currentValue = {key: key as string, label: this.labelFromKey(key as string)}; - } - return currentValue; - } - - labelFromKey(key: string): string { - let label = key || ''; - if (key) { - for (const item of this.props.form.items) { - if (item.value === key) { - label = item.label; - break; - } - } - } - return label; - } - - arrayValues(items: KeyLabelItem[]): string[] { - const v: string[] = []; - if (items) { - items.forEach(item => { - v.push(item.key); - }); - } - return v; - } - - keyIndex(values: KeyLabelItem[], key: string): number { - let index = -1; - if (values) { - for (let i = 0; i < values.length; i++) { - if (values[i].key === key) { - index = i; - break; - } - } - } - return index; - } - - onSelect(value: KeyLabelItem) { - if (this.props.form.schema.type === 'array') { - const v = this.state.currentValue as KeyLabelItem[]; - v.push(this.keyToCurrentValue(value.key, false) as KeyLabelItem); - this.setState({ - currentValue: v - }); - this.props.onChangeValidate(this.arrayValues(v)); - } else { - this.setState({currentValue: this.keyToCurrentValue(value.key, false)}); - this.props.onChangeValidate({target: {value: value.key}}); - } - } - - onDeselect(value: KeyLabelItem) { - if (this.props.form.schema.type === 'array') { - const v = this.state.currentValue as KeyLabelItem[]; - const index = this.keyIndex(v, value.key); - if (index > -1) { - v.splice(index, 1); - } - this.setState({ - currentValue: v - }); - this.props.onChangeValidate(this.arrayValues(v)); - } - } - - onBlur() { - this.setState({ focused: false }); - } - - onFocus() { - this.setState({ focused: true }); - } - - render() { - - let options: React.JSX.Element[] = []; - if (this.state.items && this.state.items.length > 0) { - options = this.state.items.map((item) => ( - - )); - } - - let labelClass = 'tb-label'; - if (this.props.form.required) { - labelClass += ' tb-required'; - } - if (this.props.form.readonly) { - labelClass += ' tb-readonly'; - } - if (this.state.focused) { - labelClass += ' tb-focused'; - } - let mode: Mode; - let value = this.state.currentValue; - if (this.props.form.tags || this.props.form.multiple) { - value = deepClone(value); - if (this.props.form.tags) { - mode = 'tags'; - } else if (this.props.form.multiple) { - mode = 'multiple'; - } - } - - const dropdownStyle = {...this.props.form.dropdownStyle, ...{zIndex: 100001}}; - let dropdownClassName = 'tb-rc-select-dropdown'; - if (this.props.form.dropdownClassName) { - dropdownClassName += ' ' + this.props.form.dropdownClassName; - } - - return ( -
    - - -
    {this.props.error}
    -
    - ); - } -} - -export default ThingsboardBaseComponent(ThingsboardRcSelect); diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-react.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-react.tsx deleted file mode 100644 index a56054a614..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-react.tsx +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 * as React from 'react'; -import { createTheme, ThemeProvider } from '@mui/material/styles'; -import thingsboardTheme from './styles/thingsboardTheme'; -import ThingsboardSchemaForm from './json-form-schema-form'; -import { JsonFormProps } from './json-form.models'; - -const tbTheme = createTheme(thingsboardTheme); - -class ReactSchemaForm extends React.Component { - - static defaultProps: JsonFormProps; - - constructor(props) { - super(props); - } - - render() { - if (this.props.form.length > 0) { - return ; - } else { - return
    ; - } - } -} - -ReactSchemaForm.defaultProps = { - isFullscreen: false, - schema: {}, - form: ['*'], - groupInfoes: [], - option: { - formDefaults: { - startEmpty: true - } - } -}; - -export default ReactSchemaForm; diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-schema-form.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-schema-form.tsx deleted file mode 100644 index bbdd90cbe7..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-schema-form.tsx +++ /dev/null @@ -1,217 +0,0 @@ -/* - * 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 * as React from 'react'; -import JsonFormUtils from './json-form-utils'; - -import ThingsboardArray from './json-form-array'; -import ThingsboardJavaScript from './json-form-javascript'; -import ThingsboardJson from './json-form-json'; -import ThingsboardHtml from './json-form-html'; -import ThingsboardCss from './json-form-css'; -import ThingsboardColor from './json-form-color'; -import ThingsboardRcSelect from './json-form-rc-select'; -import ThingsboardNumber from './json-form-number'; -import ThingsboardText from './json-form-text'; -import ThingsboardSelect from './json-form-select'; -import ThingsboardRadios from './json-form-radios'; -import ThingsboardDate from './json-form-date'; -import ThingsboardImage from './json-form-image'; -import ThingsboardCheckbox from './json-form-checkbox'; -import ThingsboardHelp from './json-form-help'; -import ThingsboardFieldSet from './json-form-fieldset'; -import ThingsboardIcon from './json-form-icon'; -import { - JsonFormData, - JsonFormProps, - onChangeFn, - OnColorClickFn, onHelpClickFn, - OnIconClickFn, - onToggleFullscreenFn -} from './json-form.models'; - -import _ from 'lodash'; -import tinycolor from 'tinycolor2'; -import { GroupInfo } from '@shared/models/widget.models'; -import ThingsboardMarkdown from '@shared/components/json-form/react/json-form-markdown'; -import { MouseEvent, ReactNode } from 'react'; - -class ThingsboardSchemaForm extends React.Component { - - private hasConditions: boolean; - private conditionFunction: Function; - private readonly mapper: {[type: string]: any}; - - constructor(props: JsonFormProps) { - super(props); - - this.mapper = { - number: ThingsboardNumber, - text: ThingsboardText, - password: ThingsboardText, - textarea: ThingsboardText, - select: ThingsboardSelect, - radios: ThingsboardRadios, - date: ThingsboardDate, - image: ThingsboardImage, - checkbox: ThingsboardCheckbox, - help: ThingsboardHelp, - array: ThingsboardArray, - javascript: ThingsboardJavaScript, - json: ThingsboardJson, - html: ThingsboardHtml, - css: ThingsboardCss, - markdown: ThingsboardMarkdown, - color: ThingsboardColor, - 'rc-select': ThingsboardRcSelect, - fieldset: ThingsboardFieldSet, - icon: ThingsboardIcon - }; - - this.onChange = this.onChange.bind(this); - this.onColorClick = this.onColorClick.bind(this); - this.onIconClick = this.onIconClick.bind(this); - this.onToggleFullscreen = this.onToggleFullscreen.bind(this); - this.onHelpClick = this.onHelpClick.bind(this); - this.hasConditions = false; - } - - onChange(key: (string | number)[], val: any, forceUpdate?: boolean) { - this.props.onModelChange(key, val, forceUpdate); - if (this.hasConditions) { - this.forceUpdate(); - } - } - - onColorClick(key: (string | number)[], val: tinycolor.ColorFormats.RGBA, - colorSelectedFn: (color: tinycolor.ColorFormats.RGBA) => void) { - this.props.onColorClick(key, val, colorSelectedFn); - } - - onIconClick(key: (string | number)[], val: string, - iconSelectedFn: (icon: string) => void) { - this.props.onIconClick(key, val, iconSelectedFn); - } - - onToggleFullscreen(fullscreenFinishFn?: (el: Element) => void) { - this.props.onToggleFullscreen(fullscreenFinishFn); - } - - onHelpClick(event: MouseEvent, helpId: string, helpVisibleFn: (visible: boolean) => void, helpReadyFn: (ready: boolean) => void) { - this.props.onHelpClick(event, helpId, helpVisibleFn, helpReadyFn); - } - - - builder(form: JsonFormData, - model: any, - index: number, - onChange: onChangeFn, - onColorClick: OnColorClickFn, - onIconClick: OnIconClickFn, - onToggleFullscreen: onToggleFullscreenFn, - onHelpClick: onHelpClickFn, - mapper: {[type: string]: any}): React.JSX.Element { - const type = form.type; - const Field = this.mapper[type]; - if (!Field) { - console.log('Invalid field: \"' + form.key[0] + '\"!'); - return null; - } - if (form.condition) { - this.hasConditions = true; - if (!form.conditionFunction) { - form.conditionFunction = new Function('form', 'model', 'index', `return ${form.condition};`); - } - if (form.conditionFunction(form, model, index) === false) { - return null; - } - } - return ; - } - - createSchema(theForm: any[]): React.JSX.Element { - const merged = JsonFormUtils.merge(this.props.schema, theForm, this.props.ignore, this.props.option); - let mapper = this.mapper; - if (this.props.mapper) { - mapper = _.merge(this.mapper, this.props.mapper); - } - const forms: ReactNode[] = merged.map(function(form: JsonFormData, index: number) { - return this.builder(form, this.props.model, index, this.onChange, this.onColorClick, - this.onIconClick, this.onToggleFullscreen, this.onHelpClick, mapper); - }.bind(this)); - - let formClass = 'SchemaForm'; - if (this.props.isFullscreen) { - formClass += ' SchemaFormFullscreen'; - } - - return ( -
    {forms}
    - ); - } - - render() { - if (this.props.groupInfoes && this.props.groupInfoes.length > 0) { - const content: React.JSX.Element[] = []; - for (const info of this.props.groupInfoes) { - const forms = this.createSchema(this.props.form[info.formIndex]); - const item = ; - content.push(item); - } - return (
    {content}
    ); - } else { - return this.createSchema(this.props.form); - } - } -} -export default ThingsboardSchemaForm; - -interface ThingsboardSchemaGroupProps { - info: GroupInfo; - forms: React.JSX.Element; -} - -interface ThingsboardSchemaGroupState { - showGroup: boolean; -} - -class ThingsboardSchemaGroup extends React.Component { - constructor(props: ThingsboardSchemaGroupProps) { - super(props); - this.state = { - showGroup: true - }; - } - - toogleGroup() { - this.setState({ - showGroup: !this.state.showGroup - }); - } - - render() { - const theCla = 'pull-right fa fa-chevron-down tb-toggle-icon' + (this.state.showGroup ? '' : ' tb-toggled'); - return (
    -
    {this.props.info.GroupTitle}
    -
    {this.props.forms}
    -
    ); - } -} diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-select.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-select.tsx deleted file mode 100644 index 46da881e38..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-select.tsx +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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 * as React from 'react'; -import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; -import MenuItem from '@mui/material/MenuItem'; -import FormControl from '@mui/material/FormControl'; -import InputLabel from '@mui/material/InputLabel'; -import Select, { SelectChangeEvent } from '@mui/material/Select'; -import ThingsboardBaseComponent from '@shared/components/json-form/react/json-form-base-component'; -import { isObject } from '@core/utils'; - -interface ThingsboardSelectState extends JsonFormFieldState { - currentValue: any; -} - -class ThingsboardSelect extends React.Component { - - static getDerivedStateFromProps(props: JsonFormFieldProps) { - if (props.model && props.form.key) { - return { - currentValue: ThingsboardSelect.getModelKey(props.model, props.form.key) - || (props.form.titleMap != null ? props.form.titleMap[0].value : '') - } - } - } - - static getModelKey(model: any, key: (string | number)[]) { - if (Array.isArray(key)) { - const res = key.reduce((cur, nxt) => (cur[nxt] || {}), model); - if (res && isObject(res)) { - return undefined; - } else { - return res; - } - } else { - return model[key]; - } - } - - constructor(props: JsonFormFieldProps) { - super(props); - this.onSelected = this.onSelected.bind(this); - const possibleValue = ThingsboardSelect.getModelKey(this.props.model, this.props.form.key); - this.state = { - currentValue: this.props.model !== undefined && possibleValue ? possibleValue : this.props.form.titleMap != null ? - this.props.form.titleMap[0].value : '' - }; - } - - onSelected(event: SelectChangeEvent) { - - this.setState({ - currentValue: event.target.value - }); - this.props.onChangeValidate(event); - } - - render() { - const menuItems = this.props.form.titleMap.map((item, idx) => ( - {item.name} - )); - - return ( - - {this.props.form.title} - - - ); - } -} - -export default ThingsboardBaseComponent(ThingsboardSelect); diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-text.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-text.tsx deleted file mode 100644 index 6fad3f3480..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-text.tsx +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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 * as React from 'react'; -import ThingsboardBaseComponent from './json-form-base-component'; -import TextField from '@mui/material/TextField'; -import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; - -interface ThingsboardTextState extends JsonFormFieldState { - focused: boolean; -} - -class ThingsboardText extends React.Component { - - constructor(props: JsonFormFieldProps) { - super(props); - this.onBlur = this.onBlur.bind(this); - this.onFocus = this.onFocus.bind(this); - this.state = { - focused: false - }; - } - - onBlur() { - this.setState({focused: false}); - } - - onFocus() { - this.setState({focused: true}); - } - - render() { - - let fieldClass = 'tb-field'; - if (this.props.form.required) { - fieldClass += ' tb-required'; - } - if (this.props.form.readonly) { - fieldClass += ' tb-readonly'; - } - if (this.state.focused) { - fieldClass += ' tb-focused'; - } - - const multiline = this.props.form.type === 'textarea'; - let rows = 1; - let rowsMax = 1; - let minHeight = 48; - if (multiline) { - rows = this.props.form.rows || 2; - rowsMax = this.props.form.rowsMax; - minHeight = 19 * rows + 48; - } - - return ( -
    - { - this.props.onChangeValidate(e); - }} - defaultValue={this.props.value} - disabled={this.props.form.readonly} - rows={rows} - maxRows={rowsMax} - onFocus={this.onFocus} - onBlur={this.onBlur} - style={this.props.form.style || {width: '100%', minHeight: minHeight + 'px'}}/> -
    - ); - } -} - -export default ThingsboardBaseComponent(ThingsboardText); diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form.models.ts b/ui-ngx/src/app/shared/components/json-form/react/json-form.models.ts deleted file mode 100644 index ffcf201136..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form.models.ts +++ /dev/null @@ -1,141 +0,0 @@ -/// -/// 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 tinycolor from 'tinycolor2'; -import { GroupInfo } from '@shared/models/widget.models'; -import { MouseEvent } from 'react'; - -export interface SchemaValidationResult { - valid: boolean; - error?: { - message?: string; - }; -} - -export interface FormOption { - formDefaults?: { - startEmpty?: boolean; - readonly?: boolean; - }; - supressPropertyTitles?: boolean; -} - -export interface DefaultsFormOptions { - global?: FormOption; - required?: boolean; - path?: string[]; - lookup?: {[key: string]: any}; - ignore?: {[key: string]: boolean}; -} - -export type onChangeFn = (key: (string | number)[], val: any, forceUpdate?: boolean) => void; -export type OnColorClickFn = (key: (string | number)[], val: tinycolor.ColorFormats.RGBA, - colorSelectedFn: (color: tinycolor.ColorFormats.RGBA) => void) => void; -export type OnIconClickFn = (key: (string | number)[], val: string, - iconSelectedFn: (icon: string) => void) => void; -export type onToggleFullscreenFn = (fullscreenFinishFn?: (el: Element) => void) => void; -export type onHelpClickFn = (event: MouseEvent, helpId: string, helpVisibleFn: (visible: boolean) => void, - helpReadyFn: (ready: boolean) => void) => void; - -export interface JsonFormProps { - model?: any; - schema?: any; - form?: any; - groupInfoes?: GroupInfo[]; - isFullscreen: boolean; - ignore?: {[key: string]: boolean}; - option: FormOption; - onModelChange?: onChangeFn; - onColorClick?: OnColorClickFn; - onIconClick?: OnIconClickFn; - onToggleFullscreen?: onToggleFullscreenFn; - onHelpClick?: onHelpClickFn; - mapper?: {[type: string]: any}; -} - -export interface KeyLabelItem { - key: string; - label: string; - value?: string; -} - -export interface JsonSchemaData { - type: string; - default: any; - items?: JsonSchemaData; - properties?: any; -} - -export interface JsonFormData { - type: string; - key: (string | number)[]; - title: string; - readonly: boolean; - required: boolean; - default?: any; - condition?: string; - conditionFunction?: Function; - style?: any; - rows?: number; - rowsMax?: number; - placeholder?: string; - schema: JsonSchemaData; - titleMap: { - value: any; - name: string; - }[]; - items?: Array | Array; - tabs?: Array; - tags?: any; - helpId?: string; - startEmpty?: boolean; - [key: string]: any; -} - -export type ComponentBuilderFn = (form: JsonFormData, - model: any, - index: number, - onChange: onChangeFn, - onColorClick: OnColorClickFn, - onIconClick: OnIconClickFn, - onToggleFullscreen: onToggleFullscreenFn, - onHelpClick: onHelpClickFn, - mapper: {[type: string]: any}) => JSX.Element; - -export interface JsonFormFieldProps { - value: any; - model: any; - form: JsonFormData; - builder: ComponentBuilderFn; - mapper?: {[type: string]: any}; - onChange?: onChangeFn; - onColorClick?: OnColorClickFn; - onIconClick?: OnIconClickFn; - onChangeValidate?: (e: any, forceUpdate?: boolean) => void; - onToggleFullscreen?: onToggleFullscreenFn; - onHelpClick?: onHelpClickFn; - valid?: boolean; - error?: string; - options?: { - setSchemaDefaults?: boolean; - }; -} - -export interface JsonFormFieldState { - value?: any; - valid?: boolean; - error?: string; -} diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form.scss b/ui-ngx/src/app/shared/components/json-form/react/json-form.scss deleted file mode 100644 index 42e1dc9677..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form.scss +++ /dev/null @@ -1,361 +0,0 @@ -/** - * 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. - */ -$swift-ease-out-duration: .4s !default; -$swift-ease-out-timing-function: cubic-bezier(.25, .8, .25, 1) !default; - -$input-label-float-offset: 6px !default; -$input-label-float-scale: .75 !default; - -$previewSize: 100px !default; - -.tb-json-form { - - &.tb-fullscreen { - background: #fff; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - > div.fullscreen-form-field { - position: relative; - width: 100%; - height: 100%; - } - } - - .json-form-error { - position: relative; - bottom: -5px; - font-size: 12px; - line-height: 12px; - color: rgb(244, 67, 54); - - transition: all 450ms cubic-bezier(.23, 1, .32, 1) 0ms; - } - - .tb-container { - position: relative; - box-sizing: border-box; - padding: 10px 0; - margin-top: 32px; - } - - .tb-field { - padding-bottom: 18px; - - .MuiInputBase-multiline { - flex: 1; - flex-direction: column; - .MuiInputBase-inputMultiline { - flex: 1; - } - } - - &.tb-required { - label::after { - font-size: 13px; - color: rgba(0, 0, 0, .54); - vertical-align: top; - content: " *"; - } - } - - &.tb-focused:not(.tb-readonly) { - label::after { - color: rgb(221, 44, 0); - } - } - } - - .tb-date-field { - &.tb-required { - div > div:first-child::after { - font-size: 13px; - color: rgba(0, 0, 0, .54); - vertical-align: top; - content: " *"; - } - } - - &.tb-focused:not(.tb-readonly) { - div > div:first-child::after { - color: rgb(221, 44, 0); - } - } - } - - label.tb-label { - position: absolute; - right: auto; - bottom: 100%; - left: 0; - color: rgba(0, 0, 0, .54); - - transition: transform $swift-ease-out-timing-function $swift-ease-out-duration, width $swift-ease-out-timing-function $swift-ease-out-duration; - - transform: translate3d(0, $input-label-float-offset, 0) scale($input-label-float-scale); - transform-origin: left top; - -webkit-font-smoothing: antialiased; - - &.tb-focused { - color: rgb(96, 125, 139); - } - - &.tb-required::after { - font-size: 13px; - color: rgba(0, 0, 0, .54); - vertical-align: top; - content: " *"; - } - - &.tb-focused:not(.tb-readonly)::after { - color: rgb(221, 44, 0); - } - } - - .tb-head-label { - color: rgba(0, 0, 0, .54); - padding-bottom: 15px; - } - - .SchemaGroupname { - padding: 10px 20px; - background-color: #f1f1f1; - } - - .invisible { - display: none; - } - - .tb-button-toggle .tb-toggle-icon { - display: inline-block; - width: 15px; - margin: auto 0 auto auto; - background-size: 100% auto; - - transition: transform .3s, ease-in-out; - } - - .tb-button-toggle .tb-toggle-icon.tb-toggled { - transform: rotateZ(180deg); - } - - .fullscreen-form-field { - .json-form-ace-editor { - height: calc(100% - 60px); - } - } - - .json-form-ace-editor { - position: relative; - height: 100%; - border: 1px solid #c0c0c0; - - .title-panel { - position: absolute; - top: 10px; - right: 20px; - z-index: 5; - font-size: .8rem; - font-weight: 500; - - label { - padding: 4px; - color: #00acc1; - background: rgba(220, 220, 220, .35); - border-radius: 5px; - } - - button.tidy-button { - background: rgba(220, 220, 220, .35) !important; - - span { - padding: 0 !important; - font-size: 12px !important; - } - } - button.help-button { - background: rgba(220, 220, 220, .35); - padding: 4px; - } - div.help-button-loading { - pointer-events: none; - background: #f3f3f3; - border-radius: 50%; - display: flex; - place-content: center; - align-items: center; - } - } - } - - .tb-image-select-container { - position: relative; - width: 100%; - height: $previewSize; - } - - .tb-image-preview { - width: auto; - max-width: $previewSize; - height: auto; - max-height: $previewSize; - } - - .tb-image-preview-container { - position: relative; - float: left; - width: $previewSize; - height: $previewSize; - margin-right: 12px; - vertical-align: top; - border: solid 1px; - - div { - width: 100%; - font-size: 18px; - text-align: center; - } - - div, .tb-image-preview { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - } - } - - .tb-dropzone { - outline: none; - position: relative; - height: $previewSize; - padding: 0 8px; - overflow: hidden; - vertical-align: top; - border: dashed 2px; - - div { - position: absolute; - top: 50%; - left: 50%; - width: 100%; - font-size: 24px; - text-align: center; - transform: translate(-50%, -50%); - } - } - - .tb-image-clear-container { - position: relative; - float: right; - width: 48px; - height: $previewSize; - } - - .tb-image-clear-btn { - position: absolute !important; - top: 50%; - transform: translate(0%, -50%) !important; - } - - .MuiButton-root { - text-transform: none; - } - -} - -.rc-select { - box-sizing: border-box; - display: inline-block; - position: relative; - vertical-align: middle; - color: #666; - line-height: 28px; - font-size: inherit !important; - .rc-select-selector { - outline: none; - user-select: none; - box-sizing: border-box; - display: block; - background-color: #fff; - border-radius: 6px; - } - &.rc-select-single { - &:not(.rc-select-customize-input) { - .rc-select-selector { - height: 28px; - line-height: 28px; - position: relative; - border: 1px solid #d9d9d9; - &:hover { - border-color: #23c0fa; - box-shadow: 0 0 2px rgba(45, 183, 245, 0.8); - } - .rc-select-selection-search { - .rc-select-selection-search-input { - cursor: pointer; - background: transparent; - margin-left: 10px; - } - } - .rc-select-selection-item, .rc-select-selection-placeholder { - top: 0; - left: 10px; - } - } - &.rc-select-focused { - .rc-select-selector { - border-color: #23c0fa !important; - box-shadow: 0 0 2px rgba(45, 183, 245, 0.8) !important; - } - } - } - } -} - -.rc-select-dropdown { - &.tb-rc-select-dropdown { - z-index: 100001; - background-color: white; - border: 1px solid #d9d9d9; - box-shadow: 0 0 4px #d9d9d9; - border-radius: 4px; - box-sizing: border-box; - outline: none; - - .rc-select-item { - &.rc-select-item-option { - margin: 0; - position: relative; - display: block; - padding: 7px 10px; - font-weight: normal; - color: #666; - white-space: nowrap; - &.rc-select-item-option-selected { - color: #666; - background-color: #ddd; - } - &.rc-select-item-option-active { - background-color: #5897fb; - color: white; - cursor: pointer; - } - } - } - } -} diff --git a/ui-ngx/src/app/shared/components/json-form/react/styles/thingsboardTheme.ts b/ui-ngx/src/app/shared/components/json-form/react/styles/thingsboardTheme.ts deleted file mode 100644 index ab3497fd53..0000000000 --- a/ui-ngx/src/app/shared/components/json-form/react/styles/thingsboardTheme.ts +++ /dev/null @@ -1,46 +0,0 @@ -/// -/// 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 { indigo, deepOrange } from '@mui/material/colors'; -import { ThemeOptions } from '@mui/material/styles'; -import { PaletteOptions } from '@mui/material/styles/createPalette'; -import { mergeDeep } from '@core/utils'; - -const PRIMARY_COLOR = '#305680'; -const SECONDARY_COLOR = '#527dad'; -const HUE3_COLOR = '#a7c1de'; - -const tbIndigo = mergeDeep({}, indigo, { - 500: PRIMARY_COLOR, - 600: SECONDARY_COLOR, - 700: PRIMARY_COLOR, - A100: HUE3_COLOR -}); - -const thingsboardPalette: PaletteOptions = { - primary: tbIndigo, - secondary: deepOrange, - background: { - default: '#eee' - } -}; - -export default { - typography: { - fontFamily: 'Roboto, \'Helvetica Neue\', sans-serif' - }, - palette: thingsboardPalette, -} as ThemeOptions; diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-utils.ts b/ui-ngx/src/app/shared/legacy/json-form-utils.ts similarity index 70% rename from ui-ngx/src/app/shared/components/json-form/react/json-form-utils.ts rename to ui-ngx/src/app/shared/legacy/json-form-utils.ts index d75819b73d..87e0afb31c 100644 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-utils.ts +++ b/ui-ngx/src/app/shared/legacy/json-form-utils.ts @@ -14,63 +14,12 @@ /// limitations under the License. /// -import * as tv from 'tv4'; import ObjectPath from 'objectpath'; import _ from 'lodash'; import { DefaultsFormOptions, - FormOption, - JsonFormData, - JsonSchemaData, - SchemaValidationResult + FormOption } from './json-form.models'; -import { isDefined, isEqual, isString, isUndefined } from '@core/utils'; - -function validateBySchema(schema: any, value: any): SchemaValidationResult { - return tv.validateResult(value, schema); -} - -function validate(form: any, value: any): SchemaValidationResult { - - if (!form) { - return {valid: true}; - } - const schema = form.schema; - - if (!schema) { - return {valid: true}; - } - - if (value === '') { - value = undefined; - } - - // Numbers fields will give a null value, which also means empty field - if (form.type === 'number' && value === null) { - value = undefined; - } - - if (form.type === 'number' && isNaN(parseFloat(value))) { - value = undefined; - } - const wrap: any = {type: 'object', properties: {}}; - const propName = form.key[form.key.length - 1]; - wrap.properties[propName] = schema; - - if (form.required) { - wrap.required = [propName]; - } - const valueWrap = {}; - if (typeof value !== 'undefined') { - valueWrap[propName] = value; - } - - const tv4Result: SchemaValidationResult = tv.validateResult(valueWrap, wrap); - if (tv4Result != null && !tv4Result.valid && form.validationMessage != null && typeof value !== 'undefined') { - tv4Result.error.message = form.validationMessage; - } - return tv4Result; -} function stripNullType(type: any): string { if (Array.isArray(type) && type.length === 2) { @@ -438,155 +387,7 @@ function merge(schema: any, form: any[], ignore: { [key: string]: boolean }, opt })); } -function selectOrSet(projection: string | (string | number)[], obj: any, valueToSet?: any): any { - const numRe = /^\d+$/; - - if (!obj) { - obj = this; - } - const parts = typeof projection === 'string' ? ObjectPath.parse(projection) : projection; - - if (typeof valueToSet !== 'undefined' && parts.length === 1) { - obj[parts[0]] = valueToSet; - return obj; - } - - if (typeof valueToSet !== 'undefined' && typeof obj[parts[0]] === 'undefined') { - obj[parts[0]] = parts.length > 2 && numRe.test(parts[1]) ? [] : {}; - } - - let value = obj[parts[0]]; - for (let i = 1; i < parts.length; i++) { - if (parts[i] === '') { - return undefined; - } - if (typeof valueToSet !== 'undefined') { - if (i === parts.length - 1) { - value[parts[i]] = valueToSet; - return valueToSet; - } else { - let tmp = value[parts[i]]; - if (typeof tmp === 'undefined' || tmp === null) { - tmp = numRe.test(parts[i + 1]) ? [] : {}; - value[parts[i]] = tmp; - } - value = tmp; - } - } else if (value) { - value = value[parts[i]]; - } - } - return value; -} - -function updateValue(projection: string | (string | number)[], obj: any, valueToSet: any): boolean { - const numRe = /^\d+$/; - - if (!obj) { - obj = this; - } - - if (!obj) { - return false; - } - - const parts: string[] = isString(projection) ? ObjectPath.parse(projection) : projection; - - if (parts.length === 1) { - return setValue(obj, parts[0], valueToSet); - } - - if (isUndefined(obj[parts[0]])) { - obj[parts[0]] = parts.length > 2 && numRe.test(parts[1]) ? [] : {}; - } - - let value = obj[parts[0]]; - for (let i = 1; i < parts.length; i++) { - if (parts[i] === '') { - return false; - } - if (i === parts.length - 1) { - return setValue(value, parts[i], valueToSet); - } else { - let tmp = value[parts[i]]; - if (isUndefined(tmp) || tmp === null) { - tmp = numRe.test(parts[i + 1]) ? [] : {}; - value[parts[i]] = tmp; - } - value = tmp; - } - } - return value; -} - - -function setValue(obj: any, key: string, val: any): boolean { - let changed = false; - if (obj) { - if (isUndefined(val)) { - if (isDefined(obj[key])) { - delete obj[key]; - changed = true; - } - } else { - changed = !isEqual(obj[key], val); - obj[key] = val; - } - } - return changed; -} - -function traverseSchema(schema: JsonSchemaData, fn: (prop: any, path: string[]) => any, path?: string[], ignoreArrays?: boolean) { - ignoreArrays = typeof ignoreArrays !== 'undefined' ? ignoreArrays : true; - - path = path || []; - - const traverse = ($schema: JsonSchemaData, $fn: (prop: any, path: string[]) => any, $path: string[]) => { - $fn($schema, $path); - if ($schema.properties) { - for (const k of Object.keys($schema.properties)) { - if ($schema.properties.hasOwnProperty(k)) { - const currentPath = $path.slice(); - currentPath.push(k); - traverse($schema.properties[k], $fn, currentPath); - } - } - } - if (!ignoreArrays && $schema.items) { - const arrPath = $path.slice(); - arrPath.push(''); - traverse($schema.items, $fn, arrPath); - } - }; - - traverse(schema, fn, path || []); -} - -function traverseForm(form: JsonFormData, fn: (form: JsonFormData) => any) { - fn(form); - if (form.items) { - form.items.forEach((f) => { - traverseForm(f, fn); - }); - } - - if (form.tabs) { - form.tabs.forEach((tab) => { - tab.items.forEach((f) => { - traverseForm(f, fn); - }); - }); - } -} - - const utils = { - validateBySchema, - validate, - merge, - updateValue, - selectOrSet, - traverseSchema, - traverseForm + merge }; export default utils; diff --git a/ui-ngx/src/app/shared/legacy/json-form.models.ts b/ui-ngx/src/app/shared/legacy/json-form.models.ts new file mode 100644 index 0000000000..ffd2830133 --- /dev/null +++ b/ui-ngx/src/app/shared/legacy/json-form.models.ts @@ -0,0 +1,88 @@ +/// +/// 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. +/// + +export interface FormOption { + formDefaults?: { + startEmpty?: boolean; + readonly?: boolean; + }; + supressPropertyTitles?: boolean; +} + +export interface DefaultsFormOptions { + global?: FormOption; + required?: boolean; + path?: string[]; + lookup?: {[key: string]: any}; + ignore?: {[key: string]: boolean}; +} + +export interface KeyLabelItem { + key: string; + label: string; + value?: string; +} + +export interface JsonSchemaData { + type: string; + default: any; + items?: JsonSchemaData; + properties?: any; +} + +export interface JsonFormData { + type: string; + key: (string | number)[]; + title: string; + readonly: boolean; + required: boolean; + default?: any; + condition?: string; + conditionFunction?: Function; + style?: any; + rows?: number; + rowsMax?: number; + placeholder?: string; + schema: JsonSchemaData; + titleMap: { + value: any; + name: string; + }[]; + items?: Array | Array; + tabs?: Array; + tags?: any; + helpId?: string; + startEmpty?: boolean; + [key: string]: any; +} + +export interface GroupInfo { + formIndex: number; + GroupTitle: string; +} + +export interface JsonSchema { + type: string; + title?: string; + properties: {[key: string]: any}; + required?: string[]; +} + +export interface JsonSettingsSchema { + schema?: JsonSchema; + form?: any[]; + groupInfoes?: GroupInfo[]; +} diff --git a/ui-ngx/src/app/shared/models/dynamic-form.models.ts b/ui-ngx/src/app/shared/models/dynamic-form.models.ts index 14df1ff3b2..bb7f134c28 100644 --- a/ui-ngx/src/app/shared/models/dynamic-form.models.ts +++ b/ui-ngx/src/app/shared/models/dynamic-form.models.ts @@ -17,9 +17,8 @@ import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe'; import { TbEditorCompletion, TbEditorCompletions } from '@shared/models/ace/completion.models'; import { deepClone, isDefinedAndNotNull, isString } from '@core/utils'; -import { JsonSchema, JsonSettingsSchema } from '@shared/models/widget.models'; -import JsonFormUtils from '@shared/components/json-form/react/json-form-utils'; -import { JsonFormData, KeyLabelItem } from '@shared/components/json-form/react/json-form.models'; +import { JsonSchema, JsonSettingsSchema, JsonFormData, KeyLabelItem } from '@shared/legacy/json-form.models'; +import JsonFormUtils from '@shared/legacy/json-form-utils'; import { constantColor, Font } from '@shared/models/widget-settings.models'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; @@ -573,29 +572,34 @@ const formPropertyCompletionType = (property: FormProperty): string => { export const jsonFormSchemaToFormProperties = (rawSchema: string | any) : FormProperty[] => { - const properties: FormProperty[] = []; - let settingsSchema: JsonSettingsSchema; - if (!rawSchema || rawSchema === '') { - settingsSchema = {}; - } else { - settingsSchema = isString(rawSchema) ? JSON.parse(rawSchema) : rawSchema; - } - if (settingsSchema.schema) { - const schema = settingsSchema.schema; - const form = settingsSchema.form || ['*']; - const groupInfoes = settingsSchema.groupInfoes || []; - if (form.length > 0) { - if (groupInfoes.length) { - for (const info of groupInfoes) { - const theForm: any[] = form[info.formIndex]; - properties.push(...schemaFormToProperties(schema, theForm, info.GroupTitle)); + try { + const properties: FormProperty[] = []; + let settingsSchema: JsonSettingsSchema; + if (!rawSchema || rawSchema === '') { + settingsSchema = {}; + } else { + settingsSchema = isString(rawSchema) ? JSON.parse(rawSchema) : rawSchema; + } + if (settingsSchema.schema) { + const schema = settingsSchema.schema; + const form = settingsSchema.form || ['*']; + const groupInfoes = settingsSchema.groupInfoes || []; + if (form.length > 0) { + if (groupInfoes.length) { + for (const info of groupInfoes) { + const theForm: any[] = form[info.formIndex]; + properties.push(...schemaFormToProperties(schema, theForm, info.GroupTitle)); + } + } else { + properties.push(...schemaFormToProperties(schema, form)); } - } else { - properties.push(...schemaFormToProperties(schema, form)); } } + return properties; + } catch (e) { + console.warn('Failed to convert old JSON form schema to form properties:', e); + return []; } - return properties; } const schemaFormToProperties = (schema: JsonSchema, theForm: any[], groupTitle?: string): FormProperty[] => { diff --git a/ui-ngx/src/app/shared/models/widget.models.ts b/ui-ngx/src/app/shared/models/widget.models.ts index f3c82571e1..5015f6cb80 100644 --- a/ui-ngx/src/app/shared/models/widget.models.ts +++ b/ui-ngx/src/app/shared/models/widget.models.ts @@ -45,7 +45,7 @@ import { HasTenantId, HasVersion } from '@shared/models/entity.models'; import { DataKeysCallbacks, DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models'; import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-config.component.models'; import { TbFunction } from '@shared/models/js-function.models'; -import { FormProperty } from '@shared/models/dynamic-form.models'; +import { FormProperty, jsonFormSchemaToFormProperties } from '@shared/models/dynamic-form.models'; export enum widgetType { timeseries = 'timeseries', @@ -155,9 +155,8 @@ export interface WidgetTypeDescriptor { templateCss: string; controllerScript: TbFunction; settingsForm?: FormProperty[]; - settingsSchema?: string | any; - dataKeySettingsSchema?: string | any; - latestDataKeySettingsSchema?: string | any; + dataKeySettingsForm?: FormProperty[]; + latestDataKeySettingsForm?: FormProperty[]; settingsDirective?: string; dataKeySettingsDirective?: string; latestDataKeySettingsDirective?: string; @@ -196,9 +195,8 @@ export interface WidgetTypeParameters { export interface WidgetControllerDescriptor { widgetTypeFunction?: any; settingsForm?: FormProperty[]; - settingsSchema?: string | any; - dataKeySettingsSchema?: string | any; - latestDataKeySettingsSchema?: string | any; + dataKeySettingsForm?: FormProperty[]; + latestDataKeySettingsForm?: FormProperty[]; typeParameters?: WidgetTypeParameters; actionSources?: {[actionSourceId: string]: WidgetActionSource}; } @@ -238,6 +236,30 @@ export const isValidWidgetFullFqn = (fullFqn: string): boolean => { return false; }; + +export const migrateWidgetTypeToDynamicForms = (widgetType: T): T => { + const descriptor = widgetType.descriptor; + if ((descriptor as any).settingsSchema) { + if (!descriptor.settingsForm?.length) { + descriptor.settingsForm = jsonFormSchemaToFormProperties((descriptor as any).settingsSchema); + } + delete (descriptor as any).settingsSchema; + } + if ((descriptor as any).dataKeySettingsSchema) { + if (!descriptor.dataKeySettingsForm?.length) { + descriptor.dataKeySettingsForm = jsonFormSchemaToFormProperties((descriptor as any).dataKeySettingsSchema); + } + delete (descriptor as any).dataKeySettingsSchema; + } + if ((descriptor as any).latestDataKeySettingsSchema) { + if (!descriptor.latestDataKeySettingsForm?.length) { + descriptor.latestDataKeySettingsForm = jsonFormSchemaToFormProperties((descriptor as any).latestDataKeySettingsSchema); + } + delete (descriptor as any).latestDataKeySettingsSchema; + } + return widgetType; +} + export interface WidgetType extends BaseWidgetType { descriptor: WidgetTypeDescriptor; } @@ -815,24 +837,6 @@ export interface WidgetInfo extends BaseWidgetInfo { deprecated?: boolean; } -export interface GroupInfo { - formIndex: number; - GroupTitle: string; -} - -export interface JsonSchema { - type: string; - title?: string; - properties: {[key: string]: any}; - required?: string[]; -} - -export interface JsonSettingsSchema { - schema?: JsonSchema; - form?: any[]; - groupInfoes?: GroupInfo[]; -} - export interface DynamicFormData { settingsForm?: FormProperty[]; model?: any; diff --git a/ui-ngx/src/app/shared/shared.module.ts b/ui-ngx/src/app/shared/shared.module.ts index c1f243ae14..5b40dadf5f 100644 --- a/ui-ngx/src/app/shared/shared.module.ts +++ b/ui-ngx/src/app/shared/shared.module.ts @@ -121,7 +121,6 @@ import { TbJsonPipe } from '@shared/pipe/tbJson.pipe'; import { ColorPickerDialogComponent } from '@shared/components/dialog/color-picker-dialog.component'; import { ColorInputComponent } from '@shared/components/color-input.component'; import { JsFuncComponent } from '@shared/components/js-func.component'; -import { JsonFormComponent } from '@shared/components/json-form/json-form.component'; import { ConfirmDialogComponent } from '@shared/components/dialog/confirm-dialog.component'; import { AlertDialogComponent } from '@shared/components/dialog/alert-dialog.component'; import { ErrorAlertDialogComponent } from '@shared/components/dialog/error-alert-dialog.component'; @@ -362,7 +361,6 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) ColorInputComponent, MaterialIconSelectComponent, NodeScriptTestDialogComponent, - JsonFormComponent, ImageInputComponent, MultipleImageInputComponent, FileInputComponent, @@ -625,7 +623,6 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) ColorInputComponent, MaterialIconSelectComponent, NodeScriptTestDialogComponent, - JsonFormComponent, ImageInputComponent, MultipleImageInputComponent, FileInputComponent, diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index a8ed773047..875613fb6a 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -5481,9 +5481,8 @@ "tidy": "Tidy", "css": "CSS", "settings-form": "Settings form", - "settings-schema": "Settings schema", - "datakey-settings-schema": "Data key settings schema", - "latest-datakey-settings-schema": "Latest data key settings schema", + "data-key-settings-form": "Data key settings form", + "latest-data-key-settings-form": "Latest data key settings form", "widget-settings": "Widget settings", "description": "Description", "tags": "Tags", diff --git a/ui-ngx/src/styles.scss b/ui-ngx/src/styles.scss index c394a29826..dbc69fb9dd 100644 --- a/ui-ngx/src/styles.scss +++ b/ui-ngx/src/styles.scss @@ -58,6 +58,10 @@ tb-root { * TYPE DEFAULTS ***************/ +* { + box-sizing: border-box; +} + body, button, html, diff --git a/ui-ngx/src/tsconfig.app.json b/ui-ngx/src/tsconfig.app.json index 7a029aa8c7..7c7b2e93d4 100644 --- a/ui-ngx/src/tsconfig.app.json +++ b/ui-ngx/src/tsconfig.app.json @@ -3,7 +3,7 @@ "compilerOptions": { "outDir": "../out-tsc/app", "types": ["node", "jquery", "flot", "tooltipster", "tinycolor2", "js-beautify", - "react", "react-dom", "raphael", "canvas-gauges", "systemjs"] + "raphael", "canvas-gauges", "systemjs"] }, "angularCompilerOptions": { "fullTemplateTypeCheck": true, diff --git a/ui-ngx/tsconfig.json b/ui-ngx/tsconfig.json index 61712840f4..c667775ba9 100644 --- a/ui-ngx/tsconfig.json +++ b/ui-ngx/tsconfig.json @@ -14,7 +14,6 @@ "target": "ES2022", "module": "es2020", "emitDecoratorMetadata": true, - "jsx": "react", "resolveJsonModule": true, "typeRoots": [ "node_modules/@types", diff --git a/ui-ngx/yarn.lock b/ui-ngx/yarn.lock index b7e5e21e55..04c3abda1d 100644 --- a/ui-ngx/yarn.lock +++ b/ui-ngx/yarn.lock @@ -499,7 +499,7 @@ "@babel/traverse" "^7.25.9" "@babel/types" "^7.25.9" -"@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.24.7", "@babel/helper-module-imports@^7.25.9": +"@babel/helper-module-imports@^7.24.7", "@babel/helper-module-imports@^7.25.9": version "7.25.9" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715" integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== @@ -1296,7 +1296,7 @@ dependencies: regenerator-runtime "^0.14.0" -"@babel/runtime@^7.10.1", "@babel/runtime@^7.11.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.9", "@babel/runtime@^7.25.0", "@babel/runtime@^7.25.6", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": +"@babel/runtime@^7.8.4": version "7.26.0" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1" integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== @@ -1382,113 +1382,6 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.6.1.tgz#593da7a17a31a72a874e313677183334a49b01c9" integrity sha512-boghen8F0Q8D+0/Q1/1r6DUEieUJ8w2a1gIknExMSHBsJFOr2+0KUfHiVYBvucPwl3+RU5PFBK833FjFCh3BhA== -"@emotion/babel-plugin@^11.12.0": - version "11.12.0" - resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz#7b43debb250c313101b3f885eba634f1d723fcc2" - integrity sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw== - dependencies: - "@babel/helper-module-imports" "^7.16.7" - "@babel/runtime" "^7.18.3" - "@emotion/hash" "^0.9.2" - "@emotion/memoize" "^0.9.0" - "@emotion/serialize" "^1.2.0" - babel-plugin-macros "^3.1.0" - convert-source-map "^1.5.0" - escape-string-regexp "^4.0.0" - find-root "^1.1.0" - source-map "^0.5.7" - stylis "4.2.0" - -"@emotion/cache@^11.13.0", "@emotion/cache@^11.13.1": - version "11.13.1" - resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.13.1.tgz#fecfc54d51810beebf05bf2a161271a1a91895d7" - integrity sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw== - dependencies: - "@emotion/memoize" "^0.9.0" - "@emotion/sheet" "^1.4.0" - "@emotion/utils" "^1.4.0" - "@emotion/weak-memoize" "^0.4.0" - stylis "4.2.0" - -"@emotion/hash@^0.9.2": - version "0.9.2" - resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.2.tgz#ff9221b9f58b4dfe61e619a7788734bd63f6898b" - integrity sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g== - -"@emotion/is-prop-valid@^1.3.0": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz#8d5cf1132f836d7adbe42cf0b49df7816fc88240" - integrity sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw== - dependencies: - "@emotion/memoize" "^0.9.0" - -"@emotion/memoize@^0.9.0": - version "0.9.0" - resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.9.0.tgz#745969d649977776b43fc7648c556aaa462b4102" - integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ== - -"@emotion/react@11.13.3": - version "11.13.3" - resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.13.3.tgz#a69d0de2a23f5b48e0acf210416638010e4bd2e4" - integrity sha512-lIsdU6JNrmYfJ5EbUCf4xW1ovy5wKQ2CkPRM4xogziOxH1nXxBSjpC9YqbFAP7circxMfYp+6x676BqWcEiixg== - dependencies: - "@babel/runtime" "^7.18.3" - "@emotion/babel-plugin" "^11.12.0" - "@emotion/cache" "^11.13.0" - "@emotion/serialize" "^1.3.1" - "@emotion/use-insertion-effect-with-fallbacks" "^1.1.0" - "@emotion/utils" "^1.4.0" - "@emotion/weak-memoize" "^0.4.0" - hoist-non-react-statics "^3.3.1" - -"@emotion/serialize@^1.2.0", "@emotion/serialize@^1.3.0", "@emotion/serialize@^1.3.1": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.3.2.tgz#e1c1a2e90708d5d85d81ccaee2dfeb3cc0cccf7a" - integrity sha512-grVnMvVPK9yUVE6rkKfAJlYZgo0cu3l9iMC77V7DW6E1DUIrU68pSEXRmFZFOFB1QFo57TncmOcvcbMDWsL4yA== - dependencies: - "@emotion/hash" "^0.9.2" - "@emotion/memoize" "^0.9.0" - "@emotion/unitless" "^0.10.0" - "@emotion/utils" "^1.4.1" - csstype "^3.0.2" - -"@emotion/sheet@^1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.4.0.tgz#c9299c34d248bc26e82563735f78953d2efca83c" - integrity sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg== - -"@emotion/styled@11.13.0": - version "11.13.0" - resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.13.0.tgz#633fd700db701472c7a5dbef54d6f9834e9fb190" - integrity sha512-tkzkY7nQhW/zC4hztlwucpT8QEZ6eUzpXDRhww/Eej4tFfO0FxQYWRyg/c5CCXa4d/f174kqeXYjuQRnhzf6dA== - dependencies: - "@babel/runtime" "^7.18.3" - "@emotion/babel-plugin" "^11.12.0" - "@emotion/is-prop-valid" "^1.3.0" - "@emotion/serialize" "^1.3.0" - "@emotion/use-insertion-effect-with-fallbacks" "^1.1.0" - "@emotion/utils" "^1.4.0" - -"@emotion/unitless@^0.10.0": - version "0.10.0" - resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.10.0.tgz#2af2f7c7e5150f497bdabd848ce7b218a27cf745" - integrity sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg== - -"@emotion/use-insertion-effect-with-fallbacks@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz#1a818a0b2c481efba0cf34e5ab1e0cb2dcb9dfaf" - integrity sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw== - -"@emotion/utils@^1.4.0", "@emotion/utils@^1.4.1": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.4.1.tgz#b3adbb43de12ee2149541c4f1337d2eb7774f0ad" - integrity sha512-BymCXzCG3r72VKJxaYVwOXATqXIZ85cuvg0YOUDxMGNrKc1DJRZk8MgV5wyXRyEayIMd4FuXJIUgTBXvDNW5cA== - -"@emotion/weak-memoize@^0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz#5e13fac887f08c44f76b0ccaf3370eb00fec9bb6" - integrity sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg== - "@es-joy/jsdoccomment@~0.49.0": version "0.49.0" resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.49.0.tgz#e5ec1eda837c802eca67d3b29e577197f14ba1db" @@ -1676,33 +1569,6 @@ dependencies: levn "^0.4.1" -"@floating-ui/core@^1.6.0": - version "1.6.8" - resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.8.tgz#aa43561be075815879305965020f492cdb43da12" - integrity sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA== - dependencies: - "@floating-ui/utils" "^0.2.8" - -"@floating-ui/dom@^1.0.0": - version "1.6.11" - resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.11.tgz#8631857838d34ee5712339eb7cbdfb8ad34da723" - integrity sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ== - dependencies: - "@floating-ui/core" "^1.6.0" - "@floating-ui/utils" "^0.2.8" - -"@floating-ui/react-dom@^2.1.1": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.1.2.tgz#a1349bbf6a0e5cb5ded55d023766f20a4d439a31" - integrity sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A== - dependencies: - "@floating-ui/dom" "^1.0.0" - -"@floating-ui/utils@^0.2.8": - version "0.2.8" - resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.8.tgz#21a907684723bbbaa5f0974cf7730bd797eb8e62" - integrity sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig== - "@flowjs/flow.js@^2.14.1": version "2.14.1" resolved "https://registry.yarnpkg.com/@flowjs/flow.js/-/flow.js-2.14.1.tgz#267d9f9d0958f32267ea5815c2a7cc09b9219304" @@ -2150,181 +2016,6 @@ resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz#0aa5502d547b57abfc4ac492de68e2006e417242" integrity sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ== -"@mui/base@5.0.0-beta.58": - version "5.0.0-beta.58" - resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.58.tgz#66ae4e1aaef8cfd9ae81bd55a70ce76b02eb5d3e" - integrity sha512-P0E7ZrxOuyYqBvVv9w8k7wm+Xzx/KRu+BGgFcR2htTsGCpJNQJCSUXNUZ50MUmSU9hzqhwbQWNXhV1MBTl6F7A== - dependencies: - "@babel/runtime" "^7.25.0" - "@floating-ui/react-dom" "^2.1.1" - "@mui/types" "^7.2.15" - "@mui/utils" "6.0.0-rc.0" - "@popperjs/core" "^2.11.8" - clsx "^2.1.1" - prop-types "^15.8.1" - -"@mui/core-downloads-tracker@^6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.2.tgz#a15eb14d433100f734e56929f842c2ccc7cab691" - integrity sha512-1oE4U38/TtzLWRYWEm/m70dUbpcvBx0QvDVg6NtpOmSNQC1Mbx0X/rNvYDdZnn8DIsAiVQ+SZ3am6doSswUQ4g== - -"@mui/icons-material@6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-6.1.2.tgz#3e4537c312687afbdd2fd289d5412731d5da3d11" - integrity sha512-7NNcjW5JoT9jHagrVbARA1o41vQY2xezDamtke+mEKKZmsJyejfRBOacSrPDfjZQ//lyhIjNKyzAwisxYJR47w== - dependencies: - "@babel/runtime" "^7.25.6" - -"@mui/lab@6.0.0-beta.10": - version "6.0.0-beta.10" - resolved "https://registry.yarnpkg.com/@mui/lab/-/lab-6.0.0-beta.10.tgz#cf6dce21e8491aa00facc0d6b1cd357bfb2ed58e" - integrity sha512-eqCBz5SZS8Un9To3UcjH01AxkOOgvme/g0ZstFC8Nz1Kg5/EJMA0ByhKS5AvUMzUKrv0FXMdbuPqbBvF3bVrXg== - dependencies: - "@babel/runtime" "^7.25.6" - "@mui/base" "5.0.0-beta.58" - "@mui/system" "^6.1.1" - "@mui/types" "^7.2.17" - "@mui/utils" "^6.1.1" - clsx "^2.1.1" - prop-types "^15.8.1" - -"@mui/material@6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@mui/material/-/material-6.1.2.tgz#9f47bfa6adcf3b8245799cbf4c027e3cb949bcc6" - integrity sha512-5TtHeAVX9D5d2LYfB1GAUn29BcVETVsrQ76Dwb2SpAfQGW3JVy4deJCAd0RrIkI3eEUrsl0E4xuBdreszxdTTg== - dependencies: - "@babel/runtime" "^7.25.6" - "@mui/core-downloads-tracker" "^6.1.2" - "@mui/system" "^6.1.2" - "@mui/types" "^7.2.17" - "@mui/utils" "^6.1.2" - "@popperjs/core" "^2.11.8" - "@types/react-transition-group" "^4.4.11" - clsx "^2.1.1" - csstype "^3.1.3" - prop-types "^15.8.1" - react-is "^18.3.1" - react-transition-group "^4.4.5" - -"@mui/private-theming@^6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-6.1.2.tgz#1e093c7194dd9f8a511179e0e5c5b10798a4bfae" - integrity sha512-S8WcjZdNdi++8UhrrY8Lton5h/suRiQexvdTfdcPAlbajlvgM+kx+uJstuVIEyTb3gMkxzIZep87knZ0tqcR0g== - dependencies: - "@babel/runtime" "^7.25.6" - "@mui/utils" "^6.1.2" - prop-types "^15.8.1" - -"@mui/styled-engine@^6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-6.1.2.tgz#bef156ac2e47a67d49982ddb5fa4211974740a26" - integrity sha512-uKOfWkR23X39xj7th2nyTcCHqInTAXtUnqD3T5qRVdJcOPvu1rlgTleTwJC/FJvWZJBU6ieuTWDhbcx5SNViHQ== - dependencies: - "@babel/runtime" "^7.25.6" - "@emotion/cache" "^11.13.1" - "@emotion/sheet" "^1.4.0" - csstype "^3.1.3" - prop-types "^15.8.1" - -"@mui/styles@6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@mui/styles/-/styles-6.1.2.tgz#24bc654cdb0aae369348453ee8d25e3a9c1ace56" - integrity sha512-fsQkTCyyBnjsmy7CM0LG95PJZAhTsmoC/iNk4ihVYmdubMQEeGXzeAWL8E6QBChCnANmjZwm2h5ENyLnCUUuzg== - dependencies: - "@babel/runtime" "^7.25.6" - "@emotion/hash" "^0.9.2" - "@mui/private-theming" "^6.1.2" - "@mui/types" "^7.2.17" - "@mui/utils" "^6.1.2" - clsx "^2.1.1" - csstype "^3.1.3" - hoist-non-react-statics "^3.3.2" - jss "^10.10.0" - jss-plugin-camel-case "^10.10.0" - jss-plugin-default-unit "^10.10.0" - jss-plugin-global "^10.10.0" - jss-plugin-nested "^10.10.0" - jss-plugin-props-sort "^10.10.0" - jss-plugin-rule-value-function "^10.10.0" - jss-plugin-vendor-prefixer "^10.10.0" - prop-types "^15.8.1" - -"@mui/system@6.1.2", "@mui/system@^6.1.1", "@mui/system@^6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@mui/system/-/system-6.1.2.tgz#28840b04c6fc70780620759d67de2c20bdc7d1c7" - integrity sha512-mzW7F1ZMIYS1aLON48Nrk9c65OrVEVQ+R4lUcTWs1lCSul0VGK23eo4dmY0NX5PS7Oe4xz3P5B9tQZZ7SYgxcg== - dependencies: - "@babel/runtime" "^7.25.6" - "@mui/private-theming" "^6.1.2" - "@mui/styled-engine" "^6.1.2" - "@mui/types" "^7.2.17" - "@mui/utils" "^6.1.2" - clsx "^2.1.1" - csstype "^3.1.3" - prop-types "^15.8.1" - -"@mui/types@^7.2.15", "@mui/types@^7.2.17": - version "7.2.17" - resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.17.tgz#329826062d4079de5ea2b97007575cebbba1fdbc" - integrity sha512-oyumoJgB6jDV8JFzRqjBo2daUuHpzDjoO/e3IrRhhHo/FxJlaVhET6mcNrKHUq2E+R+q3ql0qAtvQ4rfWHhAeQ== - -"@mui/utils@6.0.0-rc.0": - version "6.0.0-rc.0" - resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-6.0.0-rc.0.tgz#208c12c919b5cd1731f9d14784c05c35294a893e" - integrity sha512-tBp0ILEXDL0bbDDT8PnZOjCqSm5Dfk2N0Z45uzRw+wVl6fVvloC9zw8avl+OdX1Bg3ubs/ttKn8nRNv17bpM5A== - dependencies: - "@babel/runtime" "^7.25.0" - "@mui/types" "^7.2.15" - "@types/prop-types" "^15.7.12" - clsx "^2.1.1" - prop-types "^15.8.1" - react-is "^18.3.1" - -"@mui/utils@^5.16.6": - version "5.16.6" - resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.16.6.tgz#905875bbc58d3dcc24531c3314a6807aba22a711" - integrity sha512-tWiQqlhxAt3KENNiSRL+DIn9H5xNVK6Jjf70x3PnfQPz1MPBdh7yyIcAyVBT9xiw7hP3SomRhPR7hzBMBCjqEA== - dependencies: - "@babel/runtime" "^7.23.9" - "@mui/types" "^7.2.15" - "@types/prop-types" "^15.7.12" - clsx "^2.1.1" - prop-types "^15.8.1" - react-is "^18.3.1" - -"@mui/utils@^6.1.1", "@mui/utils@^6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-6.1.2.tgz#3717cd9373324a92e48c34f74385350104be652c" - integrity sha512-6+B1YZ8cCBWD1fc3RjqpclF9UA0MLUiuXhyCO+XowD/Z2ku5IlxeEhHHlgglyBWFGMu4kib4YU3CDsG5/zVjJQ== - dependencies: - "@babel/runtime" "^7.25.6" - "@mui/types" "^7.2.17" - "@types/prop-types" "^15.7.13" - clsx "^2.1.1" - prop-types "^15.8.1" - react-is "^18.3.1" - -"@mui/x-date-pickers@7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@mui/x-date-pickers/-/x-date-pickers-7.18.0.tgz#264158195aeeaf32a00519718f6c67c165b06711" - integrity sha512-12tXIoMj9vpS8fS/bS3kWPCoVrH38vNGCxgplI0vOnUrN9rJuYJz3agLPJe1S0xciTw+9W8ZSe3soaW+owoz1Q== - dependencies: - "@babel/runtime" "^7.25.6" - "@mui/utils" "^5.16.6" - "@mui/x-internals" "7.18.0" - "@types/react-transition-group" "^4.4.11" - clsx "^2.1.1" - prop-types "^15.8.1" - react-transition-group "^4.4.5" - -"@mui/x-internals@7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@mui/x-internals/-/x-internals-7.18.0.tgz#f079968d4f7ea93e63be9faf6ba8558d6f12923b" - integrity sha512-lzCHOWIR0cAIY1bGrWSprYerahbnH5C31ql/2OWCEjcngL2NAV1M6oKI2Vp4HheqzJ822c60UyWyapvyjSzY/A== - dependencies: - "@babel/runtime" "^7.25.6" - "@mui/utils" "^5.16.6" - "@nghedgehog/core@^0.0.4": version "0.0.4" resolved "https://registry.yarnpkg.com/@nghedgehog/core/-/core-0.0.4.tgz#4e3231847d0dac557a2e2dbdf1e3a52b106dd57c" @@ -2482,32 +2173,6 @@ resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== -"@popperjs/core@^2.11.8": - version "2.11.8" - resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" - integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== - -"@rc-component/portal@^1.1.0": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@rc-component/portal/-/portal-1.1.2.tgz#55db1e51d784e034442e9700536faaa6ab63fc71" - integrity sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg== - dependencies: - "@babel/runtime" "^7.18.0" - classnames "^2.3.2" - rc-util "^5.24.4" - -"@rc-component/trigger@^2.1.1": - version "2.2.3" - resolved "https://registry.yarnpkg.com/@rc-component/trigger/-/trigger-2.2.3.tgz#b47e945115e2d0a7f7e067dbb9ed76c91c1b4385" - integrity sha512-X1oFIpKoXAMXNDYCviOmTfuNuYxE4h5laBsyCqVAVMjNHxoF3/uiyA7XdegK1XbCvBbCZ6P6byWrEoDRpKL8+A== - dependencies: - "@babel/runtime" "^7.23.2" - "@rc-component/portal" "^1.1.0" - classnames "^2.3.2" - rc-motion "^2.0.0" - rc-resize-observer "^1.3.1" - rc-util "^5.38.0" - "@rollup/rollup-android-arm-eabi@4.22.4": version "4.22.4" resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz#8b613b9725e8f9479d142970b106b6ae878610d5" @@ -3118,16 +2783,6 @@ dependencies: undici-types "~6.19.2" -"@types/parse-json@^4.0.0": - version "4.0.2" - resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" - integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== - -"@types/prop-types@*", "@types/prop-types@^15.7.12", "@types/prop-types@^15.7.13": - version "15.7.13" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.13.tgz#2af91918ee12d9d32914feb13f5326658461b451" - integrity sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA== - "@types/qs@*": version "6.9.16" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.16.tgz#52bba125a07c0482d26747d5d4947a64daf8f794" @@ -3143,28 +2798,6 @@ resolved "https://registry.yarnpkg.com/@types/raphael/-/raphael-2.3.9.tgz#d53bb8930431524f42987a8a19815c0d42a61eb5" integrity sha512-K1dZwoLNvEN+mvleFU/t2swG9Z4SE5Vub7dA5wDYojH0bVTQ8ZAP+lNsl91t1njdu/B+roSEL4QXC67I7Hpiag== -"@types/react-dom@18.3.0": - version "18.3.0" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.0.tgz#0cbc818755d87066ab6ca74fbedb2547d74a82b0" - integrity sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg== - dependencies: - "@types/react" "*" - -"@types/react-transition-group@^4.4.11": - version "4.4.11" - resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.11.tgz#d963253a611d757de01ebb241143b1017d5d63d5" - integrity sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA== - dependencies: - "@types/react" "*" - -"@types/react@*", "@types/react@18.3.10": - version "18.3.10" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.10.tgz#6edc26dc22ff8c9c226d3c7bf8357b013c842219" - integrity sha512-02sAAlBnP39JgXwkAq3PeU9DVaaGpZyF3MGcC0MKgQVkZor5IiiDAipVaxQHtDJAmO4GIy/rVBy/LzVj76Cyqg== - dependencies: - "@types/prop-types" "*" - csstype "^3.0.2" - "@types/retry@0.12.2": version "0.12.2" resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.2.tgz#ed279a64fa438bb69f2480eda44937912bb7480a" @@ -3470,7 +3103,7 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" -ace-builds@1.36.5, ace-builds@^1.32.8: +ace-builds@1.36.5: version "1.36.5" resolved "https://registry.yarnpkg.com/ace-builds/-/ace-builds-1.36.5.tgz#ae9cc7a32eccc2f484926131c00545cd6b78a6a6" integrity sha512-mZ5KVanRT6nLRDLqtG/1YQQLX/gZVC/v526cm1Ru/MTSlrbweSmqv2ZT0d2GaHpJq035MwCMIrj+LgDAUnDXrg== @@ -3790,11 +3423,6 @@ at-least-node@^1.0.0: resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== -attr-accept@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-2.2.2.tgz#646613809660110749e92f2c10833b70968d929b" - integrity sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg== - autoprefixer@10.4.20, autoprefixer@^10.4.20: version "10.4.20" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.20.tgz#5caec14d43976ef42e32dcb4bd62878e96be5b3b" @@ -3827,15 +3455,6 @@ babel-loader@9.1.3: find-cache-dir "^4.0.0" schema-utils "^4.0.0" -babel-plugin-macros@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" - integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg== - dependencies: - "@babel/runtime" "^7.12.5" - cosmiconfig "^7.0.0" - resolve "^1.19.0" - babel-plugin-polyfill-corejs2@^0.4.10: version "0.4.11" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz#30320dfe3ffe1a336c15afdcdafd6fd615b25e33" @@ -4129,11 +3748,6 @@ ci-info@^3.7.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== -classnames@2.x, classnames@^2.2.1, classnames@^2.2.6, classnames@^2.3.2: - version "2.5.1" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" - integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== - clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" @@ -4212,11 +3826,6 @@ clone@^1.0.2: resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== -clsx@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999" - integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== - color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -4363,7 +3972,7 @@ content-type@~1.0.4, content-type@~1.0.5: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== -convert-source-map@^1.5.0, convert-source-map@^1.5.1, convert-source-map@^1.7.0: +convert-source-map@^1.5.1, convert-source-map@^1.7.0: version "1.9.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== @@ -4433,17 +4042,6 @@ cose-base@^2.2.0: dependencies: layout-base "^2.0.0" -cosmiconfig@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" - integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== - dependencies: - "@types/parse-json" "^4.0.0" - import-fresh "^3.2.1" - parse-json "^5.0.0" - path-type "^4.0.0" - yaml "^1.10.0" - cosmiconfig@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-9.0.0.tgz#34c3fc58287b915f3ae905ab6dc3de258b55ad9d" @@ -4524,14 +4122,6 @@ css-select@^5.1.0: domutils "^3.0.1" nth-check "^2.0.1" -css-vendor@^2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/css-vendor/-/css-vendor-2.0.8.tgz#e47f91d3bd3117d49180a3c935e62e3d9f7f449d" - integrity sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ== - dependencies: - "@babel/runtime" "^7.8.3" - is-in-browser "^1.0.2" - css-what@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" @@ -4542,11 +4132,6 @@ cssesc@^3.0.0: resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== -csstype@^3.0.2, csstype@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" - integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== - cytoscape-cose-bilkent@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz#762fa121df9930ffeb51a495d87917c570ac209b" @@ -5062,14 +4647,6 @@ doctrine@^2.1.0: dependencies: esutils "^2.0.2" -dom-helpers@^5.0.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" - integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== - dependencies: - "@babel/runtime" "^7.8.7" - csstype "^3.0.2" - dom-serializer@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" @@ -5703,13 +5280,6 @@ file-entry-cache@^8.0.0: dependencies: flat-cache "^4.0.0" -file-selector@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.6.0.tgz#fa0a8d9007b829504db4d07dd4de0310b65287dc" - integrity sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw== - dependencies: - tslib "^2.4.0" - fill-range@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" @@ -5745,11 +5315,6 @@ find-replace@^3.0.0: dependencies: array-back "^3.0.1" -find-root@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" - integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== - find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -6126,13 +5691,6 @@ hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: dependencies: function-bind "^1.1.2" -hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" - integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== - dependencies: - react-is "^16.7.0" - hosted-git-info@^7.0.0: version "7.0.2" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-7.0.2.tgz#9b751acac097757667f30114607ef7b661ff4f17" @@ -6267,11 +5825,6 @@ hyperdyperid@^1.2.0: resolved "https://registry.yarnpkg.com/hyperdyperid/-/hyperdyperid-1.2.0.tgz#59668d323ada92228d2a869d3e474d5a33b69e6b" integrity sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A== -hyphenate-style-name@^1.0.3: - version "1.1.0" - resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz#1797bf50369588b47b72ca6d5e65374607cf4436" - integrity sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw== - iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -6511,11 +6064,6 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" -is-in-browser@^1.0.2, is-in-browser@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835" - integrity sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g== - is-inside-container@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" @@ -6746,7 +6294,7 @@ js-cookie@^3.0.5: resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.5.tgz#0b7e2fd0c01552c58ba86e0841f94dc2557dcdbc" integrity sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw== -"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: +js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== @@ -6861,76 +6409,6 @@ jsonparse@^1.3.1: resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== -jss-plugin-camel-case@^10.10.0: - version "10.10.0" - resolved "https://registry.yarnpkg.com/jss-plugin-camel-case/-/jss-plugin-camel-case-10.10.0.tgz#27ea159bab67eb4837fa0260204eb7925d4daa1c" - integrity sha512-z+HETfj5IYgFxh1wJnUAU8jByI48ED+v0fuTuhKrPR+pRBYS2EDwbusU8aFOpCdYhtRc9zhN+PJ7iNE8pAWyPw== - dependencies: - "@babel/runtime" "^7.3.1" - hyphenate-style-name "^1.0.3" - jss "10.10.0" - -jss-plugin-default-unit@^10.10.0: - version "10.10.0" - resolved "https://registry.yarnpkg.com/jss-plugin-default-unit/-/jss-plugin-default-unit-10.10.0.tgz#db3925cf6a07f8e1dd459549d9c8aadff9804293" - integrity sha512-SvpajxIECi4JDUbGLefvNckmI+c2VWmP43qnEy/0eiwzRUsafg5DVSIWSzZe4d2vFX1u9nRDP46WCFV/PXVBGQ== - dependencies: - "@babel/runtime" "^7.3.1" - jss "10.10.0" - -jss-plugin-global@^10.10.0: - version "10.10.0" - resolved "https://registry.yarnpkg.com/jss-plugin-global/-/jss-plugin-global-10.10.0.tgz#1c55d3c35821fab67a538a38918292fc9c567efd" - integrity sha512-icXEYbMufiNuWfuazLeN+BNJO16Ge88OcXU5ZDC2vLqElmMybA31Wi7lZ3lf+vgufRocvPj8443irhYRgWxP+A== - dependencies: - "@babel/runtime" "^7.3.1" - jss "10.10.0" - -jss-plugin-nested@^10.10.0: - version "10.10.0" - resolved "https://registry.yarnpkg.com/jss-plugin-nested/-/jss-plugin-nested-10.10.0.tgz#db872ed8925688806e77f1fc87f6e62264513219" - integrity sha512-9R4JHxxGgiZhurDo3q7LdIiDEgtA1bTGzAbhSPyIOWb7ZubrjQe8acwhEQ6OEKydzpl8XHMtTnEwHXCARLYqYA== - dependencies: - "@babel/runtime" "^7.3.1" - jss "10.10.0" - tiny-warning "^1.0.2" - -jss-plugin-props-sort@^10.10.0: - version "10.10.0" - resolved "https://registry.yarnpkg.com/jss-plugin-props-sort/-/jss-plugin-props-sort-10.10.0.tgz#67f4dd4c70830c126f4ec49b4b37ccddb680a5d7" - integrity sha512-5VNJvQJbnq/vRfje6uZLe/FyaOpzP/IH1LP+0fr88QamVrGJa0hpRRyAa0ea4U/3LcorJfBFVyC4yN2QC73lJg== - dependencies: - "@babel/runtime" "^7.3.1" - jss "10.10.0" - -jss-plugin-rule-value-function@^10.10.0: - version "10.10.0" - resolved "https://registry.yarnpkg.com/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.10.0.tgz#7d99e3229e78a3712f78ba50ab342e881d26a24b" - integrity sha512-uEFJFgaCtkXeIPgki8ICw3Y7VMkL9GEan6SqmT9tqpwM+/t+hxfMUdU4wQ0MtOiMNWhwnckBV0IebrKcZM9C0g== - dependencies: - "@babel/runtime" "^7.3.1" - jss "10.10.0" - tiny-warning "^1.0.2" - -jss-plugin-vendor-prefixer@^10.10.0: - version "10.10.0" - resolved "https://registry.yarnpkg.com/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.10.0.tgz#c01428ef5a89f2b128ec0af87a314d0c767931c7" - integrity sha512-UY/41WumgjW8r1qMCO8l1ARg7NHnfRVWRhZ2E2m0DMYsr2DD91qIXLyNhiX83hHswR7Wm4D+oDYNC1zWCJWtqg== - dependencies: - "@babel/runtime" "^7.3.1" - css-vendor "^2.0.8" - jss "10.10.0" - -jss@10.10.0, jss@^10.10.0: - version "10.10.0" - resolved "https://registry.yarnpkg.com/jss/-/jss-10.10.0.tgz#a75cc85b0108c7ac8c7b7d296c520a3e4fbc6ccc" - integrity sha512-cqsOTS7jqPsPMjtKYDUpdFC0AbhYFLTcuGRqymgmdJIeQ8cH7+AgX7YSgQy79wXloZq2VvATYxUOUQEvS1V/Zw== - dependencies: - "@babel/runtime" "^7.3.1" - csstype "^3.0.2" - is-in-browser "^1.1.3" - tiny-warning "^1.0.2" - jstree-bootstrap-theme@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/jstree-bootstrap-theme/-/jstree-bootstrap-theme-1.0.1.tgz#7d5edc73a846e8da7f94f57a1cc5ddee9d9eab4b" @@ -7221,22 +6699,12 @@ lodash.debounce@^4.0.8: resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== -lodash.get@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" - integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== - -lodash.isequal@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" - integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== - lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash@4.17.21, lodash@^4.0.1, lodash@^4.17.14: +lodash@4.17.21, lodash@^4.17.14: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -7265,13 +6733,6 @@ log-update@^6.1.0: strip-ansi "^7.1.0" wrap-ansi "^9.0.0" -loose-envify@^1.1.0, loose-envify@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== - dependencies: - js-tokens "^3.0.0 || ^4.0.0" - lru-cache@^10.0.1, lru-cache@^10.2.0: version "10.4.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" @@ -7913,7 +7374,7 @@ nth-check@^2.0.1: dependencies: boolbase "^1.0.0" -object-assign@^4.0.1, object-assign@^4.1.1: +object-assign@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== @@ -8185,7 +7646,7 @@ parse-imports@^2.1.1: es-module-lexer "^1.5.3" slashes "^3.0.12" -parse-json@^5.0.0, parse-json@^5.2.0: +parse-json@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -8292,11 +7753,6 @@ path-to-regexp@0.1.10: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b" integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w== -path-type@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== - path-type@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-5.0.0.tgz#14b01ed7aea7ddf9c7c3f46181d4d04f9c785bb8" @@ -8505,11 +7961,6 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prettier@^2.8.3: - version "2.8.8" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" - integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== - prismjs@^1.27.0, prismjs@^1.28.0: version "1.29.0" resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12" @@ -8538,15 +7989,6 @@ promise-retry@^2.0.1: err-code "^2.0.2" retry "^0.12.0" -prop-types@^15.6.2, prop-types@^15.8.1: - version "15.8.1" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" - integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== - dependencies: - loose-envify "^1.4.0" - object-assign "^4.1.1" - react-is "^16.13.1" - proto-list@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" @@ -8632,128 +8074,6 @@ rbush@^3.0.1: dependencies: quickselect "^2.0.0" -rc-motion@^2.0.0, rc-motion@^2.0.1: - version "2.9.3" - resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.9.3.tgz#b1bdaf816f1ccb3e4b3b0c531c3037a59286379e" - integrity sha512-rkW47ABVkic7WEB0EKJqzySpvDqwl60/tdkY7hWP7dYnh5pm0SzJpo54oW3TDUGXV5wfxXFmMkxrzRRbotQ0+w== - dependencies: - "@babel/runtime" "^7.11.1" - classnames "^2.2.1" - rc-util "^5.43.0" - -rc-overflow@^1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/rc-overflow/-/rc-overflow-1.3.2.tgz#72ee49e85a1308d8d4e3bd53285dc1f3e0bcce2c" - integrity sha512-nsUm78jkYAoPygDAcGZeC2VwIg/IBGSodtOY3pMof4W3M9qRJgqaDYm03ZayHlde3I6ipliAxbN0RUcGf5KOzw== - dependencies: - "@babel/runtime" "^7.11.1" - classnames "^2.2.1" - rc-resize-observer "^1.0.0" - rc-util "^5.37.0" - -rc-resize-observer@^1.0.0, rc-resize-observer@^1.3.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.4.0.tgz#7bba61e6b3c604834980647cce6451914750d0cc" - integrity sha512-PnMVyRid9JLxFavTjeDXEXo65HCRqbmLBw9xX9gfC4BZiSzbLXKzW3jPz+J0P71pLbD5tBMTT+mkstV5gD0c9Q== - dependencies: - "@babel/runtime" "^7.20.7" - classnames "^2.2.1" - rc-util "^5.38.0" - resize-observer-polyfill "^1.5.1" - -rc-select@14.15.2: - version "14.15.2" - resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.15.2.tgz#d85fcf3a708bdf837b003feeed653347b8980ad0" - integrity sha512-oNoXlaFmpqXYcQDzcPVLrEqS2J9c+/+oJuGrlXeVVX/gVgrbHa5YcyiRUXRydFjyuA7GP3elRuLF7Y3Tfwltlw== - dependencies: - "@babel/runtime" "^7.10.1" - "@rc-component/trigger" "^2.1.1" - classnames "2.x" - rc-motion "^2.0.1" - rc-overflow "^1.3.1" - rc-util "^5.16.1" - rc-virtual-list "^3.5.2" - -rc-util@^5.15.0, rc-util@^5.16.1, rc-util@^5.24.4, rc-util@^5.37.0, rc-util@^5.38.0, rc-util@^5.43.0: - version "5.43.0" - resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.43.0.tgz#bba91fbef2c3e30ea2c236893746f3e9b05ecc4c" - integrity sha512-AzC7KKOXFqAdIBqdGWepL9Xn7cm3vnAmjlHqUnoQaTMZYhM4VlXGLkkHHxj/BZ7Td0+SOPKB4RGPboBVKT9htw== - dependencies: - "@babel/runtime" "^7.18.3" - react-is "^18.2.0" - -rc-virtual-list@3.5.2, rc-virtual-list@^3.5.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.5.2.tgz#5e1028869bae900eacbae6788d4eca7210736006" - integrity sha512-sE2G9hTPjVmatQni8OP2Kx33+Oth6DMKm67OblBBmgMBJDJQOOFpSGH7KZ6Pm85rrI2IGxDRXZCr0QhYOH2pfQ== - dependencies: - "@babel/runtime" "^7.20.0" - classnames "^2.2.6" - rc-resize-observer "^1.0.0" - rc-util "^5.15.0" - -react-ace@12.0.0: - version "12.0.0" - resolved "https://registry.yarnpkg.com/react-ace/-/react-ace-12.0.0.tgz#d40afc7382092109eead7227d9426f55dcc2209d" - integrity sha512-PstU6CSMfYIJknb4su2Fa0WgLXzq2ufQgR6fjcSWuGT1hGTHkBzuKw+SncV8PuLCdSJBJc1VehPhyeXlWByG/g== - dependencies: - ace-builds "^1.32.8" - diff-match-patch "^1.0.5" - lodash.get "^4.4.2" - lodash.isequal "^4.5.0" - prop-types "^15.8.1" - -react-dom@18.3.1: - version "18.3.1" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" - integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== - dependencies: - loose-envify "^1.1.0" - scheduler "^0.23.2" - -react-dropzone@14.2.9: - version "14.2.9" - resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-14.2.9.tgz#193a33f9035e29fc91abf24e50de5d66cfa7c8c0" - integrity sha512-jRZsMC7h48WONsOLHcmhyn3cRWJoIPQjPApvt/sJVfnYaB3Qltn025AoRTTJaj4WdmmgmLl6tUQg1s0wOhpodQ== - dependencies: - attr-accept "^2.2.2" - file-selector "^0.6.0" - prop-types "^15.8.1" - -react-is@^16.13.1, react-is@^16.7.0: - version "16.13.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" - integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== - -react-is@^18.2.0, react-is@^18.3.1: - version "18.3.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" - integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== - -react-transition-group@^4.4.5: - version "4.4.5" - resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" - integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== - dependencies: - "@babel/runtime" "^7.5.5" - dom-helpers "^5.0.1" - loose-envify "^1.4.0" - prop-types "^15.6.2" - -react@18.3.1: - version "18.3.1" - resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" - integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== - dependencies: - loose-envify "^1.1.0" - -reactcss@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/reactcss/-/reactcss-1.2.3.tgz#c00013875e557b1cf0dfd9a368a1c3dab3b548dd" - integrity sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A== - dependencies: - lodash "^4.0.1" - read-cache@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" @@ -8888,11 +8208,6 @@ requires-port@^1.0.0: resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== -resize-observer-polyfill@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" - integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== - resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" @@ -8909,7 +8224,7 @@ resolve-url-loader@5.0.0: postcss "^8.2.14" source-map "0.6.1" -resolve@1.22.8, resolve@^1.1.7, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.22.4, resolve@^1.22.8: +resolve@1.22.8, resolve@^1.1.7, resolve@^1.14.2, resolve@^1.22.4, resolve@^1.22.8: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== @@ -9092,13 +8407,6 @@ sax@^1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== -scheduler@^0.23.2: - version "0.23.2" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3" - integrity sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ== - dependencies: - loose-envify "^1.1.0" - schema-inspector@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/schema-inspector/-/schema-inspector-2.1.0.tgz#85096fbc78162a420262ed41b82e60ac927767b2" @@ -9408,11 +8716,6 @@ source-map@0.7.4: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== -source-map@^0.5.7: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== - spdx-correct@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" @@ -9621,11 +8924,6 @@ strip-json-comments@3.1.1, strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -stylis@4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51" - integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw== - stylis@^4.3.1: version "4.3.4" resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.4.tgz#ca5c6c4a35c4784e4e93a2a24dc4e9fa075250a4" @@ -9810,11 +9108,6 @@ tiny-emitter@^2.0.0: resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== -tiny-warning@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" - integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== - tinycolor2@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.6.0.tgz#f98007460169b0263b97072c5ae92484ce02d09e" @@ -10504,11 +9797,6 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^1.10.0: - version "1.10.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" - integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== - yaml@^2.2.2, yaml@^2.3.4: version "2.6.1" resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.6.1.tgz#42f2b1ba89203f374609572d5349fb8686500773" From f6071177ae955b34ab107bc4f2d4bb56ef08e935 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 23 Dec 2024 16:48:20 +0200 Subject: [PATCH 026/108] UI: Ability to import form properties from JSON content. Update help resources according to new widget settings forms. --- .../dynamic-form-properties.component.ts | 9 ++- .../home/pages/widget/widget-editor.models.ts | 20 +++++- .../import-dialog.component.html | 19 +++++- .../import-export/import-dialog.component.ts | 39 ++++++++++-- .../import-export/import-export.service.ts | 10 ++- .../models/ace/widget-completion.models.ts | 4 +- .../app/shared/models/dynamic-form.models.ts | 61 ++++++++++++++++++- .../widget/editor/examples/alarm_widget.md | 35 +++++------ .../widget/editor/examples/rpc_widget.md | 41 ++++++------- .../widget/editor/examples/static_widget.md | 29 ++++----- .../help/en_US/widget/editor/widget_js_fn.md | 26 ++++---- .../editor/widget_js_subscription_object.md | 4 +- .../assets/locale/locale.constant-en_US.json | 3 +- 13 files changed, 204 insertions(+), 96 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.ts index 262d6c57a7..d7b5c2f7a1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-properties.component.ts @@ -37,7 +37,12 @@ import { } from '@angular/forms'; import { CdkDragDrop } from '@angular/cdk/drag-drop'; import { TranslateService } from '@ngx-translate/core'; -import { FormProperty, FormPropertyType, propertyValid } from '@shared/models/dynamic-form.models'; +import { + cleanupFormProperties, + FormProperty, + FormPropertyType, + propertyValid +} from '@shared/models/dynamic-form.models'; import { DynamicFormPropertyRowComponent } from '@home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-row.component'; @@ -134,7 +139,7 @@ export class DynamicFormPropertiesComponent implements ControlValueAccessor, OnI controls[i].patchValue(p, {emitEvent: false}); } }); - this.propagateChange(properties); + this.propagateChange(cleanupFormProperties(properties)); } ); } diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.models.ts b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.models.ts index b8175704ca..eb4783b095 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.models.ts +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.models.ts @@ -54,8 +54,24 @@ const widgetEditorCompletions = (settingsCompletions?: TbEditorCompletions): TbE description: 'Called when widget element is destroyed. Should be used to cleanup all resources if necessary.', meta: 'function' }, + getSettingsForm: { + description: 'Optional function returning widget settings form array as alternative to Settings form tab of settings section.', + meta: 'function', + return: { + description: 'An array of widget settings form properties', + type: 'Array<FormProperty>' + } + }, + getDataKeySettingsForm: { + description: 'Optional function returning particular data key settings form array as alternative to Data key settings form tab of settings section.', + meta: 'function', + return: { + description: 'An array of data key settings form properties', + type: 'Array<FormProperty>' + } + }, getSettingsSchema: { - description: 'Optional function returning widget settings schema json as alternative to Settings tab of Settings schema section.', + description: 'Deprecated. Use getSettingsForm() function.', meta: 'function', return: { description: 'An widget settings schema json', @@ -63,7 +79,7 @@ const widgetEditorCompletions = (settingsCompletions?: TbEditorCompletions): TbE } }, getDataKeySettingsSchema: { - description: 'Optional function returning particular data key settings schema json as alternative to Data key settings schema of Settings schema section.', + description: 'Deprecated. Use getDataKeySettingsForm() function.', meta: 'function', return: { description: 'A particular data key settings schema json', diff --git a/ui-ngx/src/app/shared/import-export/import-dialog.component.html b/ui-ngx/src/app/shared/import-export/import-dialog.component.html index c472fb2526..8bbe1599c4 100644 --- a/ui-ngx/src/app/shared/import-export/import-dialog.component.html +++ b/ui-ngx/src/app/shared/import-export/import-dialog.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
    +

    {{ importTitle }}

    @@ -30,15 +30,28 @@
    - + {{ importFileLabel | translate }} + {{ importContentLabel | translate }} + + + +
    diff --git a/ui-ngx/src/app/shared/import-export/import-dialog.component.ts b/ui-ngx/src/app/shared/import-export/import-dialog.component.ts index b68fe3b2cd..e4256229c4 100644 --- a/ui-ngx/src/app/shared/import-export/import-dialog.component.ts +++ b/ui-ngx/src/app/shared/import-export/import-dialog.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, Inject, OnInit, SkipSelf } from '@angular/core'; +import { Component, DestroyRef, Inject, OnInit, SkipSelf } from '@angular/core'; import { ErrorStateMatcher } from '@angular/material/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { Store } from '@ngrx/store'; @@ -23,10 +23,14 @@ import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, FormGroupDire import { Router } from '@angular/router'; import { DialogComponent } from '@app/shared/components/dialog.component'; import { ActionNotificationShow } from '@core/notification/notification.actions'; +import { isDefinedAndNotNull } from '@core/utils'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export interface ImportDialogData { importTitle: string; importFileLabel: string; + enableImportFromContent?: boolean; + importContentLabel?: string; } @Component({ @@ -40,9 +44,13 @@ export class ImportDialogComponent extends DialogComponent, @@ -50,17 +58,26 @@ export class ImportDialogComponent extends DialogComponent, + private destroyRef: DestroyRef, private fb: UntypedFormBuilder) { super(store, router, dialogRef); this.importTitle = data.importTitle; this.importFileLabel = data.importFileLabel; + this.enableImportFromContent = isDefinedAndNotNull(data.enableImportFromContent) ? data.enableImportFromContent : false; + this.importContentLabel = data.importContentLabel; + } + ngOnInit(): void { this.importFormGroup = this.fb.group({ + importType: ['file'], + fileContent: [null, [Validators.required]], jsonContent: [null, [Validators.required]] }); - } - - ngOnInit(): void { + this.importFormGroup.get('importType').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.importTypeChanged(); + }); } isErrorState(control: UntypedFormControl | null, form: FormGroupDirective | NgForm | null): boolean { @@ -85,7 +102,19 @@ export class ImportDialogComponent extends DialogComponent { - return this.openImportDialog('dynamic-form.import-form', 'dynamic-form.form-json-file').pipe( + return this.openImportDialog('dynamic-form.import-form', + 'dynamic-form.json-file', true, 'dynamic-form.json-content').pipe( map((properties: FormProperty[]) => { if (!this.validateImportedFormProperties(properties)) { this.store.dispatch(new ActionNotificationShow( @@ -1134,14 +1135,17 @@ export class ImportExportService { }; } - private openImportDialog(importTitle: string, importFileLabel: string): Observable { + private openImportDialog(importTitle: string, importFileLabel: string, + enableImportFromContent = false, importContentLabel?: string): Observable { return this.dialog.open(ImportDialogComponent, { disableClose: true, panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], data: { importTitle, - importFileLabel + importFileLabel, + enableImportFromContent, + importContentLabel } }).afterClosed().pipe( map((importedData) => { diff --git a/ui-ngx/src/app/shared/models/ace/widget-completion.models.ts b/ui-ngx/src/app/shared/models/ace/widget-completion.models.ts index 4370a2cd49..d6a2c0a25f 100644 --- a/ui-ngx/src/app/shared/models/ace/widget-completion.models.ts +++ b/ui-ngx/src/app/shared/models/ace/widget-completion.models.ts @@ -325,7 +325,7 @@ export const widgetContextCompletionsWithSettings = (settingsCompletions?: TbEdi type: 'object' }, settings: { - description: 'Widget settings containing widget specific properties according to the defined settings json schema', + description: 'Widget settings containing widget specific properties according to the defined settings form.', meta: 'property', type: 'object', children: settingsCompletions @@ -363,7 +363,7 @@ export const widgetContextCompletionsWithSettings = (settingsCompletions?: TbEdi } }, settings: { - description: 'Widget settings containing widget specific properties according to the defined settings json schema', + description: 'Widget settings containing widget specific properties according to the defined settings form.', meta: 'property', type: 'object', children: settingsCompletions diff --git a/ui-ngx/src/app/shared/models/dynamic-form.models.ts b/ui-ngx/src/app/shared/models/dynamic-form.models.ts index bb7f134c28..d0ee306f89 100644 --- a/ui-ngx/src/app/shared/models/dynamic-form.models.ts +++ b/ui-ngx/src/app/shared/models/dynamic-form.models.ts @@ -16,8 +16,8 @@ import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe'; import { TbEditorCompletion, TbEditorCompletions } from '@shared/models/ace/completion.models'; -import { deepClone, isDefinedAndNotNull, isString } from '@core/utils'; -import { JsonSchema, JsonSettingsSchema, JsonFormData, KeyLabelItem } from '@shared/legacy/json-form.models'; +import { deepClone, isDefinedAndNotNull, isEmptyStr, isString, isUndefinedOrNull } from '@core/utils'; +import { JsonFormData, JsonSchema, JsonSettingsSchema, KeyLabelItem } from '@shared/legacy/json-form.models'; import JsonFormUtils from '@shared/legacy/json-form-utils'; import { constantColor, Font } from '@shared/models/widget-settings.models'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; @@ -165,6 +165,63 @@ export interface FormHtmlSection extends FormPropertyBase { export type FormProperty = FormPropertyBase & FormTextareaProperty & FormNumberProperty & FormSelectProperty & FormRadiosProperty & FormDateTimeProperty & FormJavascriptProperty & FormMarkdownProperty & FormFieldSetProperty & FormArrayProperty & FormHtmlSection; +export const cleanupFormProperties = (properties: FormProperty[]): FormProperty[] => { + for (const property of properties) { + cleanupFormProperty(property); + } + return properties; +} + +export const cleanupFormProperty = (property: FormProperty): FormProperty => { + if (property.type !== FormPropertyType.number) { + delete property.min; + delete property.max; + delete property.step; + } + if (property.type !== FormPropertyType.textarea) { + delete property.rows; + } + if (property.type !== FormPropertyType.fieldset) { + delete property.properties; + } else if (property.properties?.length) { + property.properties = cleanupFormProperties(property.properties); + } + if (property.type !== FormPropertyType.array) { + delete property.arrayItemName; + delete property.arrayItemType; + } + if (property.type !== FormPropertyType.select) { + delete property.multiple; + delete property.allowEmptyOption; + delete property.minItems; + delete property.maxItems; + } + if (property.type !== FormPropertyType.radios) { + delete property.direction; + } + if (![FormPropertyType.select, FormPropertyType.radios].includes(property.type)) { + delete property.items; + } + if (property.type !== FormPropertyType.datetime) { + delete property.allowClear; + delete property.dateTimeType; + } + if (![FormPropertyType.javascript, FormPropertyType.markdown].includes(property.type)) { + delete property.helpId; + } + if (property.type !== FormPropertyType.htmlSection) { + delete property.htmlClassList; + delete property.htmlContent; + } + for (const key of Object.keys(property)) { + const val = property[key]; + if (isUndefinedOrNull(val) || isEmptyStr(val)) { + delete property[key]; + } + } + return property; +} + export enum FormPropertyContainerType { field = 'field', row = 'row', diff --git a/ui-ngx/src/assets/help/en_US/widget/editor/examples/alarm_widget.md b/ui-ngx/src/assets/help/en_US/widget/editor/examples/alarm_widget.md index c0126214c7..9853bebd43 100644 --- a/ui-ngx/src/assets/help/en_US/widget/editor/examples/alarm_widget.md +++ b/ui-ngx/src/assets/help/en_US/widget/editor/examples/alarm_widget.md @@ -40,32 +40,25 @@ The **Widget Editor** will be opened, pre-populated with the content of the defa {:copy-code} ``` - - Put the following JSON content inside the "Settings schema" tab of **Settings schema section**: + - Import the following JSON content inside the "Settings form" tab by clicking on 'Import form from JSON' button: ```json -{ - "schema": { - "type": "object", - "title": "AlarmTableSettings", - "properties": { - "alarmSeverityColorFunction": { - "title": "Alarm severity color function: f(severity)", - "type": "string", - "default": "if(severity == 'CRITICAL') {return 'red';} else if (severity == 'MAJOR') {return 'orange';} else return 'green'; " - } - }, - "required": [] - }, - "form": [ - { - "key": "alarmSeverityColorFunction", - "type": "javascript" - } - ] -} +[ + { + "id": "alarmSeverityColorFunction", + "name": "Alarm severity color function: f(severity)", + "type": "javascript", + "default": "if (severity == 'CRITICAL') {\n return 'red';\n} else if (severity == 'MAJOR') {\n return 'orange';\n} else return 'green';", + "required": false + } +] {:copy-code} ``` + - Clear all 'form selector' fields in the "Widget settings" tab. + + - Turn off 'Has basic mode' switch in the "Widget settings" tab. + - Put the following JavaScript code inside the "JavaScript" section: ```javascript diff --git a/ui-ngx/src/assets/help/en_US/widget/editor/examples/rpc_widget.md b/ui-ngx/src/assets/help/en_US/widget/editor/examples/rpc_widget.md index 44abed8a46..7c7121cd44 100644 --- a/ui-ngx/src/assets/help/en_US/widget/editor/examples/rpc_widget.md +++ b/ui-ngx/src/assets/help/en_US/widget/editor/examples/rpc_widget.md @@ -40,35 +40,28 @@ The **Widget Editor** will open, pre-populated with default **Control** template {:copy-code} ``` - - Put the following JSON content inside the "Settings schema" tab of **Settings schema section**: + - Import the following JSON content inside the "Settings form" tab by clicking on 'Import form from JSON' button: ```json -{ - "schema": { - "type": "object", - "title": "Settings", - "properties": { - "oneWayElseTwoWay": { - "title": "Is One Way Command", - "type": "boolean", - "default": true - }, - "requestTimeout": { - "title": "RPC request timeout", - "type": "number", - "default": 500 - } - }, - "required": [] - }, - "form": [ - "oneWayElseTwoWay", - "requestTimeout" - ] -} +[ + { + "id": "oneWayElseTwoWay", + "name": "Is One Way Command", + "type": "switch", + "default": true + }, + { + "id": "requestTimeout", + "name": "RPC request timeout", + "type": "number", + "default": 500 + } +] {:copy-code} ``` + - Clear value of 'Settings form selector' in the "Widget settings" tab. + - Put the following JavaScript code inside the "JavaScript" section: ```javascript diff --git a/ui-ngx/src/assets/help/en_US/widget/editor/examples/static_widget.md b/ui-ngx/src/assets/help/en_US/widget/editor/examples/static_widget.md index d7c7901052..3a3bfe115d 100644 --- a/ui-ngx/src/assets/help/en_US/widget/editor/examples/static_widget.md +++ b/ui-ngx/src/assets/help/en_US/widget/editor/examples/static_widget.md @@ -17,28 +17,23 @@ The **Widget Editor** will be opened pre-populated with the content of default * {:copy-code} ``` - - Put the following JSON content inside the "Settings schema" tab of **Settings schema section**: + - Import the following JSON content inside the "Settings form" tab by clicking on 'Import form from JSON' button: ```json -{ - "schema": { - "type": "object", - "title": "Settings", - "properties": { - "alertContent": { - "title": "Alert content", - "type": "string", - "default": "Content derived from alertContent property of widget settings." - } - } - }, - "form": [ - "alertContent" - ] -} +[ + { + "id": "alertContent", + "name": "Alert content", + "type": "text", + "default": "Content derived from alertContent property of widget settings.", + "fieldClass": "flex" + } +] {:copy-code} ``` + - Clear value of 'Settings form selector' in the "Widget settings" tab. + - Put the following JavaScript code inside the "JavaScript" section: ```javascript diff --git a/ui-ngx/src/assets/help/en_US/widget/editor/widget_js_fn.md b/ui-ngx/src/assets/help/en_US/widget/editor/widget_js_fn.md index 32b005bedd..015bac28b4 100644 --- a/ui-ngx/src/assets/help/en_US/widget/editor/widget_js_fn.md +++ b/ui-ngx/src/assets/help/en_US/widget/editor/widget_js_fn.md @@ -10,18 +10,20 @@ Each widget function should be defined as a property of the **self** variable. In order to implement a new widget, the following JavaScript functions should be defined *(Note: each function is optional and can be implemented according to widget specific behaviour):* -|{:auto} **Function** | **Description** | -|------------------------------------|----------------------------------------------------------------------------------------| -| ``` onInit() ``` | The first function which is called when widget is ready for initialization. Should be used to prepare widget DOM, process widget settings and initial subscription information. | -| ``` onDataUpdated() ``` | Called when the new data is available from the widget subscription. Latest data can be accessed from the object of widget context (**ctx**). | -| ``` onResize() ``` | Called when widget container is resized. Latest width and height can be obtained from widget context (**ctx**). | -| ``` onEditModeChanged() ``` | Called when dashboard editing mode is changed. Latest mode is handled by isEdit property of **ctx**. | -| ``` onMobileModeChanged() ``` | Called when dashboard view width crosses mobile breakpoint. Latest state is handled by isMobile property of **ctx**. | -| ``` onDestroy() ``` | Called when widget element is destroyed. Should be used to cleanup all resources if necessary. | -| ``` getSettingsSchema() ``` | Optional function returning widget settings schema json as alternative to **Settings schema** of settings section. | -| ``` getDataKeySettingsSchema() ``` | Optional function returning particular data key settings schema json as alternative to **Data key settings schema** tab of settings section. | -| ``` typeParameters() ``` | Returns [WidgetTypeParameters{:target="_blank"}](https://github.com/thingsboard/thingsboard/blob/2627fe51d491055d4140f16617ed543f7f5bd8f6/ui-ngx/src/app/shared/models/widget.models.ts#L151) object describing widget datasource parameters. See | | -| ``` actionSources() ``` | Returns map describing available widget action sources ([WidgetActionSource{:target="_blank"}](https://github.com/thingsboard/thingsboard/blob/2627fe51d491055d4140f16617ed543f7f5bd8f6/ui-ngx/src/app/shared/models/widget.models.ts#L121)) used to define user actions. See | +| {:auto} **Function** | **Description** | +|-----------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| ``` onInit() ``` | The first function which is called when widget is ready for initialization. Should be used to prepare widget DOM, process widget settings and initial subscription information. | +| ``` onDataUpdated() ``` | Called when the new data is available from the widget subscription. Latest data can be accessed from the object of widget context (**ctx**). | +| ``` onResize() ``` | Called when widget container is resized. Latest width and height can be obtained from widget context (**ctx**). | +| ``` onEditModeChanged() ``` | Called when dashboard editing mode is changed. Latest mode is handled by isEdit property of **ctx**. | +| ``` onMobileModeChanged() ``` | Called when dashboard view width crosses mobile breakpoint. Latest state is handled by isMobile property of **ctx**. | +| ``` onDestroy() ``` | Called when widget element is destroyed. Should be used to cleanup all resources if necessary. | +| ``` getSettingsForm() ``` | Optional function returning widget settings form array as alternative to **Settings form** tab of settings section. | +| ``` getDataKeySettingsForm() ``` | Optional function returning particular data key settings form array as alternative to **Data key settings form** tab of settings section. | +| ``` typeParameters() ``` | Returns [WidgetTypeParameters{:target="_blank"}](https://github.com/thingsboard/thingsboard/blob/2627fe51d491055d4140f16617ed543f7f5bd8f6/ui-ngx/src/app/shared/models/widget.models.ts#L151) object describing widget datasource parameters. See | | +| ``` actionSources() ``` | Returns map describing available widget action sources ([WidgetActionSource{:target="_blank"}](https://github.com/thingsboard/thingsboard/blob/2627fe51d491055d4140f16617ed543f7f5bd8f6/ui-ngx/src/app/shared/models/widget.models.ts#L121)) used to define user actions. See | +| ~~getSettingsSchema()~~ | **Deprecated**. Use getSettingsForm() function. | +| ~~getDataKeySettingsSchema()~~ | **Deprecated**. Use getDataKeySettingsForm() function. |
    diff --git a/ui-ngx/src/assets/help/en_US/widget/editor/widget_js_subscription_object.md b/ui-ngx/src/assets/help/en_US/widget/editor/widget_js_subscription_object.md index edef68210d..dbf4d991f8 100644 --- a/ui-ngx/src/assets/help/en_US/widget/editor/widget_js_subscription_object.md +++ b/ui-ngx/src/assets/help/en_US/widget/editor/widget_js_subscription_object.md @@ -26,7 +26,7 @@ For [Latest values{:target="_blank"}](${siteBaseUrl}/docs${docPlatformPrefix}/us label: 'Sin', // label of the dataKey. Used as display value (for ex. in the widget legend section) color: '#ffffff', // color of the key. Can be used by widget to set color of the key data (for ex. lines in line chart or segments in the pie chart). funcBody: "", // only applicable for datasource with type "function" and "function" key type. Defines body of the function to generate simulated data. - settings: {} // dataKey specific settings with structure according to the defined Data key settings json schema. See "Settings schema section". + settings: {} // dataKey specific settings with structure according to the defined Data key settings form. }, //... ] @@ -72,7 +72,7 @@ For [Alarm widget{:target="_blank"}](${siteBaseUrl}/docs${docPlatformPrefix}/use type: 'alarm', // type of the dataKey. Only "alarm" in this case. label: 'Severity', // label of the dataKey. Used as display value (for ex. as a column title in the Alarms table) color: '#ffffff', // color of the key. Can be used by widget to set color of the key data. - settings: {} // dataKey specific settings with structure according to the defined Data key settings json schema. See "Settings schema section". + settings: {} // dataKey specific settings with structure according to the defined Data key settings form. }, //... ] diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 875613fb6a..6c4614abbb 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1699,7 +1699,8 @@ "clear-form-prompt": "Are you sure you want to remove all form properties?", "import-form": "Import form from JSON", "export-form": "Export form to JSON", - "form-json-file": "Form JSON file", + "json-file": "JSON file", + "json-content": "JSON content", "invalid-form-json-file-error": "Unable to import form from JSON: Invalid form JSON data structure." }, "asset-profile": { From d6d238733ba4b33bc32837825d8829962231e993 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 23 Dec 2024 17:37:18 +0200 Subject: [PATCH 027/108] UI: Fix compilation error after merge. --- .../scada/scada-symbol-object-settings.component.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/scada/scada-symbol-object-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/scada/scada-symbol-object-settings.component.ts index 929e6c5c37..9bdff37444 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/scada/scada-symbol-object-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/scada/scada-symbol-object-settings.component.ts @@ -21,7 +21,6 @@ import { forwardRef, Input, OnChanges, - OnDestroy, OnInit, SimpleChanges } from '@angular/core'; @@ -73,7 +72,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; } ] }) -export class ScadaSymbolObjectSettingsComponent implements OnInit, OnChanges, ControlValueAccessor, Validator, OnDestroy { +export class ScadaSymbolObjectSettingsComponent implements OnInit, OnChanges, ControlValueAccessor, Validator { ScadaSymbolBehaviorType = ScadaSymbolBehaviorType; @@ -141,13 +140,6 @@ export class ScadaSymbolObjectSettingsComponent implements OnInit, OnChanges, Co } } - ngOnDestroy() { - if (this.validatorSubscription) { - this.validatorSubscription.unsubscribe(); - this.validatorSubscription = null; - } - } - registerOnChange(fn: any): void { this.propagateChange = fn; } From 7358f1a1af2f56c50f564822865b6908ee40da1c Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 23 Dec 2024 19:04:17 +0200 Subject: [PATCH 028/108] UI: Remove unused dependency. --- ui-ngx/package.json | 1 - ui-ngx/yarn.lock | 5 ----- 2 files changed, 6 deletions(-) diff --git a/ui-ngx/package.json b/ui-ngx/package.json index 85c00e0a2e..9845d02637 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -89,7 +89,6 @@ "tinymce": "~6.8.5", "tooltipster": "^4.2.8", "tslib": "^2.7.0", - "tv4": "^1.3.0", "typeface-roboto": "^1.1.13", "zone.js": "~0.14.10" }, diff --git a/ui-ngx/yarn.lock b/ui-ngx/yarn.lock index 04c3abda1d..802ea3cf97 100644 --- a/ui-ngx/yarn.lock +++ b/ui-ngx/yarn.lock @@ -9234,11 +9234,6 @@ tuf-js@^2.2.1: debug "^4.3.4" make-fetch-happen "^13.0.1" -tv4@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/tv4/-/tv4-1.3.0.tgz#d020c846fadd50c855abb25ebaecc68fc10f7963" - integrity sha512-afizzfpJgvPr+eDkREK4MxJ/+r8nEEHcmitwgnPUqpaP+FpwQyadnxNoSACbgc/b1LsZYtODGoPiFxQrgJgjvw== - type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" From c0914772786462e0a3614e5c4bb8ec327b52fcbe Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 26 Dec 2024 12:27:07 +0200 Subject: [PATCH 029/108] UI: Fixed eslint.config.mjs - delete tsx format and disabled rules --- ui-ngx/eslint.config.mjs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ui-ngx/eslint.config.mjs b/ui-ngx/eslint.config.mjs index aaecc6d5ab..c446375607 100644 --- a/ui-ngx/eslint.config.mjs +++ b/ui-ngx/eslint.config.mjs @@ -5,11 +5,11 @@ import tailwind from "eslint-plugin-tailwindcss"; export default tsEslint.config( { - files: ["**/*.ts", "*.tsx"], + files: ["**/*.ts"], languageOptions: { parserOptions: { project: true, - tsconfigRootDir: import.meta.dirname, // or import.meta.dirname for ESM + tsconfigRootDir: import.meta.dirname }, }, extends: [ @@ -62,7 +62,9 @@ export default tsEslint.config( "@typescript-eslint/no-inferrable-types": "off", "@typescript-eslint/no-unused-vars": "off", "@typescript-eslint/ban-ts-comment": "off", - "no-case-declarations": "off" + "no-case-declarations": "off", + "no-prototype-builtins": "off", + "@typescript-eslint/consistent-type-definitions": "off" }, }, { From e5003df778701a7191a577fb784d97cfc0c47eb4 Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Mon, 30 Dec 2024 20:30:44 +0200 Subject: [PATCH 030/108] [WIP] save time series strategies: draft implementation for latest using Bloom filter --- .../DoNotSavePersistenceStrategy.java | 34 ++++++++++++ .../engine/telemetry/PersistenceStrategy.java | 35 ++++++++++++ .../SaveEveryMessagePersistenceStrategy.java | 34 ++++++++++++ ...aveFirstInIntervalPersistenceStrategy.java | 54 +++++++++++++++++++ .../engine/telemetry/TbMsgTimeseriesNode.java | 52 +++++++++++++++--- .../TbMsgTimeseriesNodeConfiguration.java | 12 ++++- .../telemetry/TbMsgTimeseriesNodeTest.java | 4 +- 7 files changed, 213 insertions(+), 12 deletions(-) create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/DoNotSavePersistenceStrategy.java create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/PersistenceStrategy.java create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/SaveEveryMessagePersistenceStrategy.java create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/SaveFirstInIntervalPersistenceStrategy.java diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/DoNotSavePersistenceStrategy.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/DoNotSavePersistenceStrategy.java new file mode 100644 index 0000000000..5652a34cd1 --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/DoNotSavePersistenceStrategy.java @@ -0,0 +1,34 @@ +/** + * 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. + */ +package org.thingsboard.rule.engine.telemetry; + +import org.thingsboard.server.common.data.kv.TsKvEntry; + +import java.util.UUID; + +public final class DoNotSavePersistenceStrategy implements PersistenceStrategy { + + public static final DoNotSavePersistenceStrategy INSTANCE = new DoNotSavePersistenceStrategy(); + + private DoNotSavePersistenceStrategy() { + } + + @Override + public boolean shouldPersist(UUID originatorUuid, TsKvEntry timeseriesEntry) { + return false; + } + +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/PersistenceStrategy.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/PersistenceStrategy.java new file mode 100644 index 0000000000..2aa92bd57c --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/PersistenceStrategy.java @@ -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. + */ +package org.thingsboard.rule.engine.telemetry; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import org.thingsboard.server.common.data.kv.TsKvEntry; + +import java.util.UUID; + +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = SaveEveryMessagePersistenceStrategy.class, name = "SAVE_EVERY_MESSAGE"), + @JsonSubTypes.Type(value = SaveFirstInIntervalPersistenceStrategy.class, name = "SAVE_FIRST_IN_INTERVAL"), + @JsonSubTypes.Type(value = DoNotSavePersistenceStrategy.class, name = "DO_NOT_SAVE") +}) +public sealed interface PersistenceStrategy permits DoNotSavePersistenceStrategy, SaveEveryMessagePersistenceStrategy, SaveFirstInIntervalPersistenceStrategy { + + // TODO: maybe this should accept generic key? + boolean shouldPersist(UUID originatorUuid, TsKvEntry timeseriesEntry); + +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/SaveEveryMessagePersistenceStrategy.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/SaveEveryMessagePersistenceStrategy.java new file mode 100644 index 0000000000..7ca564b0bc --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/SaveEveryMessagePersistenceStrategy.java @@ -0,0 +1,34 @@ +/** + * 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. + */ +package org.thingsboard.rule.engine.telemetry; + +import org.thingsboard.server.common.data.kv.TsKvEntry; + +import java.util.UUID; + +public final class SaveEveryMessagePersistenceStrategy implements PersistenceStrategy { + + public static final SaveEveryMessagePersistenceStrategy INSTANCE = new SaveEveryMessagePersistenceStrategy(); + + private SaveEveryMessagePersistenceStrategy() { + } + + @Override + public boolean shouldPersist(UUID originatorUuid, TsKvEntry timeseriesEntry) { + return true; + } + +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/SaveFirstInIntervalPersistenceStrategy.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/SaveFirstInIntervalPersistenceStrategy.java new file mode 100644 index 0000000000..4677058e6a --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/SaveFirstInIntervalPersistenceStrategy.java @@ -0,0 +1,54 @@ +/** + * 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. + */ +package org.thingsboard.rule.engine.telemetry; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.hash.BloomFilter; +import org.thingsboard.server.common.data.kv.TsKvEntry; + +import java.nio.charset.StandardCharsets; +import java.util.UUID; + +@SuppressWarnings("UnstableApiUsage") +public final class SaveFirstInIntervalPersistenceStrategy implements PersistenceStrategy { + + private final long intervalDurationMillis; + private final BloomFilter filter; + + @JsonCreator + public SaveFirstInIntervalPersistenceStrategy(@JsonProperty("intervalDurationMillis") long intervalDurationMillis) { + this.intervalDurationMillis = intervalDurationMillis; + // TODO: implement funnel as an enum + filter = BloomFilter.create((key, sink) -> + sink.putLong(key.intervalNumber()) + .putLong(key.originatorUuid().getMostSignificantBits()) + .putLong(key.originatorUuid().getLeastSignificantBits()) + .putString(key.timeseriesKey(), StandardCharsets.UTF_8), 1_000_000); + } + + // TODO: this should not be hardcoded here but should be defined by clients + // should be generified (what to do with funnel then?) + private record Key(long intervalNumber, UUID originatorUuid, String timeseriesKey) { + } + + @Override + public boolean shouldPersist(UUID originatorUuid, TsKvEntry timeseriesEntry) { + long intervalNumber = timeseriesEntry.getTs() / intervalDurationMillis; + return filter.put(new Key(intervalNumber, originatorUuid, timeseriesEntry.getKey())); + } + +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java index 27f45feb47..28f9c88e34 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java @@ -15,8 +15,12 @@ */ package org.thingsboard.rule.engine.telemetry; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.SettableFuture; import com.google.gson.JsonParser; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.common.util.DonAsynchron; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; @@ -27,6 +31,9 @@ import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; @@ -59,7 +66,7 @@ import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_RE "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", + // configDirective = "tbActionNodeTimeseriesConfig", icon = "file_upload" ) public class TbMsgTimeseriesNode implements TbNode { @@ -68,10 +75,13 @@ public class TbMsgTimeseriesNode implements TbNode { private TbContext ctx; private long tenantProfileDefaultStorageTtl; + private PersistenceStrategy latestPersistenceStrategy; + @Override public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { this.config = TbNodeUtils.convert(configuration, TbMsgTimeseriesNodeConfiguration.class); this.ctx = ctx; + latestPersistenceStrategy = config.getPersistenceConfig().latest(); ctx.addTenantProfileListener(this::onTenantProfileUpdate); onTenantProfileUpdate(ctx.getTenantProfile()); } @@ -94,10 +104,18 @@ public class TbMsgTimeseriesNode implements TbNode { ctx.tellFailure(msg, new IllegalArgumentException("Msg body is empty: " + src)); return; } - List tsKvEntryList = new ArrayList<>(); + List withLatest = new ArrayList<>(); + List withoutLatest = new ArrayList<>(); for (Map.Entry> tsKvEntry : tsKvMap.entrySet()) { for (KvEntry kvEntry : tsKvEntry.getValue()) { - tsKvEntryList.add(new BasicTsKvEntry(tsKvEntry.getKey(), kvEntry)); + TsKvEntry entry = new BasicTsKvEntry(tsKvEntry.getKey(), kvEntry); + if (latestPersistenceStrategy.shouldPersist(msg.getOriginator().getId(), entry)) { + log.info("Persisting entry: {}", entry); + withLatest.add(entry); + } else { + log.info("Skipping entry: {}", entry); + withoutLatest.add(entry); + } } } String ttlValue = msg.getMetaData().getValue("TTL"); @@ -105,15 +123,33 @@ public class TbMsgTimeseriesNode implements TbNode { if (ttl == 0L) { ttl = tenantProfileDefaultStorageTtl; } - ctx.getTelemetryService().saveTimeseries(TimeseriesSaveRequest.builder() + + SettableFuture withLatestSavedFuture = SettableFuture.create(); + TimeseriesSaveRequest saveWithLatestRequest = TimeseriesSaveRequest.builder() + .tenantId(ctx.getTenantId()) + .customerId(msg.getCustomerId()) + .entityId(msg.getOriginator()) + .entries(withLatest) + .ttl(ttl) + .saveLatest(true) + .future(withLatestSavedFuture) + .build(); + ctx.getTelemetryService().saveTimeseries(saveWithLatestRequest); + + SettableFuture withoutLatestSavedFuture = SettableFuture.create(); + TimeseriesSaveRequest saveWithoutLatestRequest = TimeseriesSaveRequest.builder() .tenantId(ctx.getTenantId()) .customerId(msg.getCustomerId()) .entityId(msg.getOriginator()) - .entries(tsKvEntryList) + .entries(withoutLatest) .ttl(ttl) - .saveLatest(!config.isSkipLatestPersistence()) - .callback(new TelemetryNodeCallback(ctx, msg)) - .build()); + .saveLatest(false) + .future(withoutLatestSavedFuture) + .build(); + ctx.getTelemetryService().saveTimeseries(saveWithoutLatestRequest); + + ListenableFuture> bothSavedFuture = Futures.allAsList(withLatestSavedFuture, withoutLatestSavedFuture); + DonAsynchron.withCallback(bothSavedFuture, success -> ctx.tellSuccess(msg), failure -> ctx.tellFailure(msg, failure)); } public static long computeTs(TbMsg msg, boolean ignoreMetadataTs) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java index 1c33778a6b..adaebb0967 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java @@ -15,6 +15,7 @@ */ package org.thingsboard.rule.engine.telemetry; +import lombok.Builder; import lombok.Data; import org.thingsboard.rule.engine.api.NodeConfiguration; @@ -22,15 +23,22 @@ import org.thingsboard.rule.engine.api.NodeConfiguration; public class TbMsgTimeseriesNodeConfiguration implements NodeConfiguration { private long defaultTTL; - private boolean skipLatestPersistence; private boolean useServerTs; + private PersistenceConfig persistenceConfig; @Override public TbMsgTimeseriesNodeConfiguration defaultConfiguration() { TbMsgTimeseriesNodeConfiguration configuration = new TbMsgTimeseriesNodeConfiguration(); configuration.setDefaultTTL(0L); - configuration.setSkipLatestPersistence(false); configuration.setUseServerTs(false); + configuration.setPersistenceConfig(PersistenceConfig.builder() + .latest(SaveEveryMessagePersistenceStrategy.INSTANCE) + .build()); return configuration; } + + @Builder + record PersistenceConfig(PersistenceStrategy latest) { + } + } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java index 2cba4b8fb3..74240d3f97 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java @@ -88,7 +88,7 @@ public class TbMsgTimeseriesNodeTest { @Test public void verifyDefaultConfig() { assertThat(config.getDefaultTTL()).isEqualTo(0L); - assertThat(config.isSkipLatestPersistence()).isFalse(); + // assertThat(config.isSkipLatestPersistence()).isFalse(); assertThat(config.isUseServerTs()).isFalse(); } @@ -162,7 +162,7 @@ public class TbMsgTimeseriesNodeTest { public void givenSkipLatestPersistenceIsTrueAndTtlFromConfig_whenOnMsg_thenSaveTimeseriesUsingTtlFromConfig() throws TbNodeException { long ttlFromConfig = 5L; config.setDefaultTTL(ttlFromConfig); - config.setSkipLatestPersistence(true); + // config.setSkipLatestPersistence(true); init(); String data = """ From 527f7176c54710b395bbb332bfed3e3a35441657 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 1 Jan 2025 14:25:28 +0100 Subject: [PATCH 031/108] tbel test refactored to not use sql and all controllers. reflection utils used --- .../script/AbstractTbelInvokeTest.java | 5 ++++- .../service/script/TbelInvokeDocsIoTest.java | 2 -- .../service/script/TbelInvokeServiceTest.java | 19 +++++++++---------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/service/script/AbstractTbelInvokeTest.java b/application/src/test/java/org/thingsboard/server/service/script/AbstractTbelInvokeTest.java index 11b61f64c4..dcd73bac50 100644 --- a/application/src/test/java/org/thingsboard/server/service/script/AbstractTbelInvokeTest.java +++ b/application/src/test/java/org/thingsboard/server/service/script/AbstractTbelInvokeTest.java @@ -16,8 +16,10 @@ package org.thingsboard.server.service.script; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.script.api.ScriptType; +import org.thingsboard.script.api.tbel.DefaultTbelInvokeService; import org.thingsboard.script.api.tbel.TbelInvokeService; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.controller.AbstractControllerTest; @@ -28,7 +30,8 @@ import java.util.concurrent.ExecutionException; import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_REQUEST; -public abstract class AbstractTbelInvokeTest extends AbstractControllerTest { +@SpringBootTest(classes = DefaultTbelInvokeService.class) +public abstract class AbstractTbelInvokeTest { @Autowired protected TbelInvokeService invokeService; diff --git a/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeDocsIoTest.java b/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeDocsIoTest.java index d72dd87992..cc51a7063c 100644 --- a/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeDocsIoTest.java +++ b/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeDocsIoTest.java @@ -18,7 +18,6 @@ package org.thingsboard.server.service.script; import org.junit.jupiter.api.Test; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.script.api.tbel.TbDate; -import org.thingsboard.server.dao.service.DaoSqlTest; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -34,7 +33,6 @@ import java.util.concurrent.atomic.AtomicReference; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertEquals; -@DaoSqlTest class TbelInvokeDocsIoTest extends AbstractTbelInvokeTest { private String decoderStr; diff --git a/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeServiceTest.java b/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeServiceTest.java index 071fdf77fc..62170236e0 100644 --- a/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeServiceTest.java @@ -22,9 +22,9 @@ import org.junit.Ignore; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Value; import org.springframework.test.context.TestPropertySource; +import org.springframework.test.util.ReflectionTestUtils; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.script.api.tbel.TbelScript; -import org.thingsboard.server.dao.service.DaoSqlTest; import java.io.Serializable; import java.util.ArrayList; @@ -38,7 +38,6 @@ import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -@DaoSqlTest @TestPropertySource(properties = { "tbel.max_script_body_size=100", "tbel.max_total_args_size=50", @@ -120,9 +119,9 @@ class TbelInvokeServiceTest extends AbstractTbelInvokeTest { scriptsIds.add(scriptId); } - Map scriptIdToHash = getFieldValue(invokeService, "scriptIdToHash"); - Map scriptMap = getFieldValue(invokeService, "scriptMap"); - Cache compiledScriptsCache = getFieldValue(invokeService, "compiledScriptsCache"); + Map scriptIdToHash = (Map) ReflectionTestUtils.getField(invokeService, "scriptIdToHash"); + Map scriptMap = (Map) ReflectionTestUtils.getField(invokeService, "scriptMap"); + Cache compiledScriptsCache = (Cache) ReflectionTestUtils.getField(invokeService, "compiledScriptsCache"); String scriptHash = scriptIdToHash.get(scriptsIds.get(0)); @@ -140,9 +139,9 @@ class TbelInvokeServiceTest extends AbstractTbelInvokeTest { scriptsIds.add(scriptId); } - Map scriptIdToHash = getFieldValue(invokeService, "scriptIdToHash"); - Map scriptMap = getFieldValue(invokeService, "scriptMap"); - Cache compiledScriptsCache = getFieldValue(invokeService, "compiledScriptsCache"); + Map scriptIdToHash = (Map) ReflectionTestUtils.getField(invokeService, "scriptIdToHash"); + Map scriptMap = (Map) ReflectionTestUtils.getField(invokeService, "scriptMap"); + Cache compiledScriptsCache = (Cache) ReflectionTestUtils.getField(invokeService, "compiledScriptsCache"); String scriptHash = scriptIdToHash.get(scriptsIds.get(0)); for (int i = 0; i < 9; i++) { @@ -163,8 +162,8 @@ class TbelInvokeServiceTest extends AbstractTbelInvokeTest { @Ignore("This test is based on assumption that Caffeine cache is LRU based but in fact it is based on " + "Tiny LFU which is the cause that the tests fail sometime: https://arxiv.org/pdf/1512.00727.pdf") public void whenCompiledScriptsCacheIsTooBig_thenRemoveRarelyUsedScripts() throws Exception { - Map scriptIdToHash = getFieldValue(invokeService, "scriptIdToHash"); - Cache compiledScriptsCache = getFieldValue(invokeService, "compiledScriptsCache"); + Map scriptIdToHash = (Map) ReflectionTestUtils.getField(invokeService, "scriptIdToHash"); + Cache compiledScriptsCache = (Cache) ReflectionTestUtils.getField(invokeService, "compiledScriptsCache"); List scriptsIds = new ArrayList<>(); for (int i = 0; i < 110; i++) { // tbel.compiled_scripts_cache_size = 100 From 1d0c998af8970c2caf543ca491a2f88a3ce9fabb Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Thu, 2 Jan 2025 12:58:28 +0200 Subject: [PATCH 032/108] UI: Add serch tag for drilling --- .../src/main/data/json/system/scada_symbols/drill-hp.svg | 3 ++- .../src/main/data/json/system/scada_symbols/hook-hp.svg | 3 ++- .../src/main/data/json/system/scada_symbols/platform-hp.svg | 3 ++- .../src/main/data/json/system/scada_symbols/preventer-hp.svg | 3 ++- .../src/main/data/json/system/scada_symbols/rotor-hp.svg | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/application/src/main/data/json/system/scada_symbols/drill-hp.svg b/application/src/main/data/json/system/scada_symbols/drill-hp.svg index 9f620109d5..66e06398ed 100644 --- a/application/src/main/data/json/system/scada_symbols/drill-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/drill-hp.svg @@ -2,7 +2,8 @@ "title": "HP Drill", "description": "Drill with various states.", "searchTags": [ - "drill" + "drill", + "drilling" ], "widgetSizeX": 1, "widgetSizeY": 1, diff --git a/application/src/main/data/json/system/scada_symbols/hook-hp.svg b/application/src/main/data/json/system/scada_symbols/hook-hp.svg index 9fdcf86208..742d1fe811 100644 --- a/application/src/main/data/json/system/scada_symbols/hook-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/hook-hp.svg @@ -2,7 +2,8 @@ "title": "HP Hook", "description": "Hook with various states.", "searchTags": [ - "hook" + "hook", + "drilling" ], "widgetSizeX": 1, "widgetSizeY": 2, diff --git a/application/src/main/data/json/system/scada_symbols/platform-hp.svg b/application/src/main/data/json/system/scada_symbols/platform-hp.svg index 2592be9d56..af9b72a2e5 100644 --- a/application/src/main/data/json/system/scada_symbols/platform-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/platform-hp.svg @@ -2,7 +2,8 @@ "title": "HP Platform", "description": "Platform with various states.", "searchTags": [ - "platform" + "platform", + "drilling" ], "widgetSizeX": 6, "widgetSizeY": 3, diff --git a/application/src/main/data/json/system/scada_symbols/preventer-hp.svg b/application/src/main/data/json/system/scada_symbols/preventer-hp.svg index a72d974241..0b193a5665 100644 --- a/application/src/main/data/json/system/scada_symbols/preventer-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/preventer-hp.svg @@ -2,7 +2,8 @@ "title": "HP Preventer", "description": "Preventer with various states.", "searchTags": [ - "preventer" + "preventer", + "drilling" ], "widgetSizeX": 2, "widgetSizeY": 1, diff --git a/application/src/main/data/json/system/scada_symbols/rotor-hp.svg b/application/src/main/data/json/system/scada_symbols/rotor-hp.svg index 095831a89e..729e85eac1 100644 --- a/application/src/main/data/json/system/scada_symbols/rotor-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/rotor-hp.svg @@ -2,7 +2,8 @@ "title": "HP Rotor", "description": "Rotor with various states.", "searchTags": [ - "rotor" + "rotor", + "drilling" ], "widgetSizeX": 2, "widgetSizeY": 1, From 6a5cc07742435fc61e7a604f70ef2f1eedd48ea5 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Thu, 2 Jan 2025 13:32:17 +0200 Subject: [PATCH 033/108] UI: Traditional SCADA symbols meter --- .../data/json/system/scada_symbols/meter.svg | 743 ++++++++++++++++++ .../system/scada_symbols/small-left-meter.svg | 717 +++++++++++++++++ .../json/system/scada_symbols/small-meter.svg | 688 ++++++++++++++++ .../scada_symbols/small-right-center.svg | 717 +++++++++++++++++ .../widget_bundles/scada_fluid_system.json | 4 + .../assets/locale/locale.constant-en_US.json | 2 + 6 files changed, 2871 insertions(+) create mode 100644 application/src/main/data/json/system/scada_symbols/meter.svg create mode 100644 application/src/main/data/json/system/scada_symbols/small-left-meter.svg create mode 100644 application/src/main/data/json/system/scada_symbols/small-meter.svg create mode 100644 application/src/main/data/json/system/scada_symbols/small-right-center.svg diff --git a/application/src/main/data/json/system/scada_symbols/meter.svg b/application/src/main/data/json/system/scada_symbols/meter.svg new file mode 100644 index 0000000000..02b2833133 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/meter.svg @@ -0,0 +1,743 @@ +{ + "title": "Meter", + "description": "Meter displays the current value with a moving pointer on the scale.", + "searchTags": [ + "scale", + "level", + "progress", + "thermometer" + ], + "widgetSizeX": 1, + "widgetSizeY": 4, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "element.attr({fill: ctx.properties.backgroundColor});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "progress-indicator", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue, initial) {\n var clampedValue = Math.max(Math.min(value, Math.max(minValue, maxValue)), Math.min(minValue, maxValue));\n var normalizedValue = minValue < maxValue\n ? (clampedValue - minValue) / (maxValue - minValue)\n : (minValue - clampedValue) / (minValue - maxValue);\n var offset = initial - (normalizedValue * initial);\n return offset;\n}\n\nvar valueSet = element.remember('valueSet');\n\nif (ctx.properties.progressArrow && !ctx.properties.progressBar) {\n element.show();\n var initial = ctx.properties.valueBox ? 329: 366;\n if (!valueSet) {\n element.remember('valueSet', true);\n element.transform({\n translateY: initial\n });\n }\n \n var minValue = ctx.properties.minValue;\n var maxValue = ctx.properties.maxValue;\n var value = ctx.values.value;\n \n var colorProcessor = ctx.properties.progressArrowColor;\n colorProcessor.update(value);\n var fill = colorProcessor.color;\n element.attr({fill: fill});\n \n var offset = calculateOffset(value, minValue, maxValue, initial);\n\n var elementOffset = element.remember('offset');\n if (offset !== elementOffset) {\n element.remember('offset', offset);\n ctx.api.cssAnimate(element, 500).transform({\n translateY: offset\n });\n }\n} else {\n element.hide();\n if (valueSet) {\n element.remember('valueSet', false);\n }\n}", + "actions": null + }, + { + "tag": "progressBar", + "stateRenderFunction": "if (ctx.properties.progressBar) {\n element.show();\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "progressBorder", + "stateRenderFunction": "if (!ctx.properties.valueBox) {\n element.attr({'height': 367})\n}", + "actions": null + }, + { + "tag": "progressCircle", + "stateRenderFunction": "if (!ctx.properties.valueBox) {\n element.attr({cy:383});\n}", + "actions": null + }, + { + "tag": "progressFill", + "stateRenderFunction": "if (!ctx.properties.valueBox) {\n element.attr({y:-378});\n}\n\nfunction calculateOffset(value, minValue, maxValue, initial) {\n var clampedValue = Math.max(Math.min(value, Math.max(minValue, maxValue)), Math.min(minValue, maxValue));\n var normalizedValue = minValue < maxValue\n ? (clampedValue - minValue) / (maxValue - minValue)\n : (minValue - clampedValue) / (minValue - maxValue);\n var offset = normalizedValue * initial;\n return offset;\n}\n\nvar valueSet = element.remember('valueSet');\nif (ctx.properties.progressBar) {\n var initial = ctx.properties.valueBox ? 329: 366;\n if (!valueSet) {\n element.remember('valueSet', true);\n element.attr({height: 2});\n }\n \n var minValue = ctx.properties.minValue;\n var maxValue = ctx.properties.maxValue;\n var value = ctx.values.value;\n \n var colorProcessor = ctx.properties.progressBarColor;\n colorProcessor.update(value);\n var fill = colorProcessor.color;\n element.attr({fill: fill, stroke: fill});\n ctx.tags.progressCircle[0].fill(fill);\n \n var height = calculateOffset(value, minValue, maxValue, initial);\n\n var elementHeight = element.remember('height');\n if (height !== elementHeight) {\n element.remember('height', height);\n ctx.api.cssAnimate(element, 500).attr({height: height+2});\n }\n} else {\n if (valueSet) {\n element.remember('valueSet', false);\n }\n}", + "actions": null + }, + { + "tag": "scale", + "stateRenderFunction": "var scaleSet = element.remember('scaleSet');\nif (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n var minValue = ctx.properties.minValue;\n var maxValue = ctx.properties.maxValue;\n \n var start = 11;\n var end = ctx.properties.valueBox ? 328 : 365;\n var majorIntervalLength = end / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n element.add(ctx.svg.line(63, end+11, 63, 11).stroke({ width: 1 }).attr({class: 'majorTick'}));\n for (var i = 0; i < majorIntervals+1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(51, y, 63, y).stroke({ width: 1 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (maxValue - ((maxValue - (minValue)) / (majorIntervals) * i)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 45, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n}\n\nvar majorFont = ctx.properties.majorFont;\nvar majorColor = ctx.properties.majorColor;\nvar minorColor = ctx.properties.minorColor;\nif (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n} else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n}\n\nvar majorTicks = element.find('line.majorTick');\nmajorTicks.forEach(t => t.attr({stroke: majorColor}));\n\nvar majorTicksText = element.find('text.majorTickText');\nctx.api.font(majorTicksText, majorFont, majorColor);\n\nvar minorTicks = element.find('line.minorTick');\nminorTicks.forEach(t => t.attr({stroke: minorColor}));\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(57, minorY, 63, minorY).stroke({ width: 1 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "actions": null + }, + { + "tag": "value-box", + "stateRenderFunction": "if (ctx.properties.valueBox) {\n element.show();\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "value-box-background", + "stateRenderFunction": "if (ctx.properties.valueBox) {\n var colorProcessor = ctx.properties.valueBoxColor;\n colorProcessor.update(ctx.values.value);\n element.attr({fill: colorProcessor.color});\n}", + "actions": null + }, + { + "tag": "value-text", + "stateRenderFunction": "if (ctx.properties.valueBox) {\n var valueTextFont = ctx.properties.valueTextFont;\n var valueTextColor = ctx.properties.valueTextColor;\n var currentVolume = ctx.values.value;\n var valueText = ctx.api.formatValue(currentVolume, 0, ctx.properties.valueUnits, false);\n var colorProcessor = ctx.properties.valueTextColor;\n colorProcessor.update(ctx.values.value);\n ctx.api.font(element, valueTextFont, colorProcessor.color);\n ctx.api.text(element, valueText);\n}", + "actions": null + } + ], + "behavior": [ + { + "id": "value", + "name": "{i18n:scada.symbol.value}", + "hint": null, + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "GET_TIME_SERIES", + "defaultValue": null, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "waterLevel" + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning-state}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical-state}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.critical-state-animation}", + "hint": "{i18n:scada.symbol.critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "minValue", + "name": "{i18n:scada.symbol.min-max-value}", + "type": "number", + "default": 0, + "required": true, + "subLabel": "{i18n:scada.symbol.min-value}", + "divider": true, + "min": -1000, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "maxValue", + "name": "{i18n:scada.symbol.min-max-value}", + "type": "number", + "default": 100, + "required": true, + "subLabel": "{i18n:scada.symbol.max-value}", + "max": 1000, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "backgroundColor", + "name": "{i18n:scada.symbol.background-color}", + "type": "color", + "default": "#FFFFFF", + "disabled": false, + "visible": true + }, + { + "id": "progressBar", + "name": "{i18n:scada.symbol.progress-bar}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "progressBarColor", + "name": "{i18n:scada.symbol.progress-bar}", + "type": "color_settings", + "default": { + "type": "constant", + "color": "#4D94E1", + "gradient": { + "advancedMode": false, + "gradient": [ + "rgba(0, 255, 0, 1)", + "rgba(255, 0, 0, 1)" + ], + "gradientAdvanced": [ + { + "source": { + "type": "constant" + }, + "color": "rgba(0, 255, 0, 1)" + }, + { + "source": { + "type": "constant" + }, + "color": "rgba(255, 0, 0, 1)" + } + ], + "minValue": 0, + "maxValue": 100 + }, + "rangeList": { + "advancedMode": false, + "range": [], + "rangeAdvanced": [] + }, + "colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';" + }, + "disableOnProperty": "progressBar", + "disabled": false, + "visible": true + }, + { + "id": "progressArrow", + "name": "{i18n:scada.symbol.progress-arrow}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, + { + "id": "progressArrowColor", + "name": "{i18n:scada.symbol.progress-arrow}", + "type": "color_settings", + "default": { + "type": "constant", + "color": "#1C943E", + "gradient": { + "advancedMode": false, + "gradient": [ + "rgba(0, 255, 0, 1)", + "rgba(255, 0, 0, 1)" + ], + "gradientAdvanced": [ + { + "source": { + "type": "constant" + }, + "color": "rgba(0, 255, 0, 1)" + }, + { + "source": { + "type": "constant" + }, + "color": "rgba(255, 0, 0, 1)" + } + ], + "minValue": 0, + "maxValue": 100 + }, + "rangeList": { + "advancedMode": false, + "range": [], + "rangeAdvanced": [] + }, + "colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';" + }, + "disableOnProperty": "progressArrow", + "disabled": true, + "visible": true + }, + { + "id": "valueBox", + "name": "{i18n:scada.symbol.value-box}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "valueBoxColor", + "name": "{i18n:scada.symbol.value-box}", + "type": "color_settings", + "default": { + "type": "constant", + "color": "#F3F3F3", + "gradient": { + "advancedMode": false, + "gradient": [ + "rgba(0, 255, 0, 1)", + "rgba(255, 0, 0, 1)" + ], + "gradientAdvanced": [ + { + "source": { + "type": "constant" + }, + "color": "rgba(0, 255, 0, 1)" + }, + { + "source": { + "type": "constant" + }, + "color": "rgba(255, 0, 0, 1)" + } + ], + "minValue": 0, + "maxValue": 100 + }, + "rangeList": { + "advancedMode": false, + "range": [], + "rangeAdvanced": [] + }, + "colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';" + }, + "disableOnProperty": "valueBox", + "disabled": false, + "visible": true + }, + { + "id": "valueUnits", + "name": "{i18n:scada.symbol.value-text}", + "type": "units", + "default": "%", + "subLabel": "{i18n:scada.symbol.units}", + "disableOnProperty": "valueBox", + "disabled": false, + "visible": true + }, + { + "id": "valueTextFont", + "name": "{i18n:scada.symbol.value-text}", + "type": "font", + "default": { + "size": 14, + "sizeUnit": "px", + "family": "Roboto", + "weight": "500", + "style": "normal" + }, + "disableOnProperty": "valueBox", + "disabled": false, + "visible": true + }, + { + "id": "valueTextColor", + "name": "{i18n:scada.symbol.value-text}", + "type": "color_settings", + "default": { + "type": "constant", + "color": "#0000008A", + "gradient": { + "advancedMode": false, + "gradient": [ + "rgba(0, 255, 0, 1)", + "rgba(255, 0, 0, 1)" + ], + "gradientAdvanced": [ + { + "source": { + "type": "constant" + }, + "color": "rgba(0, 255, 0, 1)" + }, + { + "source": { + "type": "constant" + }, + "color": "rgba(255, 0, 0, 1)" + } + ], + "minValue": 0, + "maxValue": 100 + }, + "rangeList": { + "advancedMode": false, + "range": [], + "rangeAdvanced": [] + }, + "colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';" + }, + "disableOnProperty": "valueBox", + "disabled": false, + "visible": true + }, + { + "id": "majorIntervals", + "name": "{i18n:scada.symbol.major-ticks}", + "type": "number", + "default": 10, + "subLabel": "{i18n:scada.symbol.intervals}", + "divider": true, + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "majorFont", + "name": "{i18n:scada.symbol.major-ticks}", + "type": "font", + "default": { + "size": 12, + "sizeUnit": "px", + "family": "Roboto", + "weight": "500", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "majorColor", + "name": "{i18n:scada.symbol.major-ticks-color}", + "type": "color", + "default": "#747474", + "subLabel": "{i18n:scada.symbol.normal}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "majorWarningColor", + "name": "{i18n:scada.symbol.major-ticks-color}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "majorCriticalColor", + "name": "{i18n:scada.symbol.major-ticks-color}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + }, + { + "id": "minorIntervals", + "name": "{i18n:scada.symbol.minor-ticks}", + "type": "number", + "default": 10, + "subLabel": "{i18n:scada.symbol.intervals}", + "min": 1, + "disabled": false, + "visible": true + }, + { + "id": "minorColor", + "name": "{i18n:scada.symbol.minor-ticks-color}", + "type": "color", + "default": "#747474", + "subLabel": "{i18n:scada.symbol.normal}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "minorWarningColor", + "name": "{i18n:scada.symbol.minor-ticks-color}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "minorCriticalColor", + "name": "{i18n:scada.symbol.minor-ticks-color}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + } + ] +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 37% + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/small-left-meter.svg b/application/src/main/data/json/system/scada_symbols/small-left-meter.svg new file mode 100644 index 0000000000..a480412366 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/small-left-meter.svg @@ -0,0 +1,717 @@ +{ + "title": "Small left meter", + "description": "Small left meter displays the current value with a moving pointer on the scale.", + "searchTags": [ + "scale", + "level", + "progress", + "thermometer" + ], + "widgetSizeX": 1, + "widgetSizeY": 2, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "element.attr({fill: ctx.properties.backgroundColor});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "progress-indicator", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue, initial) {\n var clampedValue = Math.max(Math.min(value, Math.max(minValue, maxValue)), Math.min(minValue, maxValue));\n var normalizedValue = minValue < maxValue\n ? (clampedValue - minValue) / (maxValue - minValue)\n : (minValue - clampedValue) / (minValue - maxValue);\n var offset = initial - (normalizedValue * initial);\n return offset;\n}\n\nvar valueSet = element.remember('valueSet');\n\nif (ctx.properties.progressArrow && !ctx.properties.progressBar) {\n element.show();\n var initial = ctx.properties.valueBox ? 135 : 168;\n if (!valueSet) {\n element.remember('valueSet', true);\n element.transform({\n translateY: initial\n });\n }\n \n var minValue = ctx.properties.minValue;\n var maxValue = ctx.properties.maxValue;\n var value = ctx.values.value;\n \n var colorProcessor = ctx.properties.progressArrowColor;\n colorProcessor.update(value);\n var fill = colorProcessor.color;\n element.attr({fill: fill});\n \n var offset = calculateOffset(value, minValue, maxValue, initial);\n\n var elementOffset = element.remember('offset');\n if (offset !== elementOffset) {\n element.remember('offset', offset);\n ctx.api.cssAnimate(element, 500).transform({\n translateY: offset\n });\n }\n} else {\n element.hide();\n if (valueSet) {\n element.remember('valueSet', false);\n }\n}", + "actions": null + }, + { + "tag": "progressBar", + "stateRenderFunction": "if (ctx.properties.progressBar) {\n element.show();\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "progressBorder", + "stateRenderFunction": "if (ctx.properties.valueBox) {\n element.attr({'height': 137})\n}", + "actions": null + }, + { + "tag": "progressCircle", + "stateRenderFunction": "if (ctx.properties.valueBox) {\n element.attr({cy:152});\n}", + "actions": null + }, + { + "tag": "progressFill", + "stateRenderFunction": "if (ctx.properties.valueBox) {\n element.attr({y:-147});\n}\n\nfunction calculateOffset(value, minValue, maxValue, initial) {\n var clampedValue = Math.max(Math.min(value, Math.max(minValue, maxValue)), Math.min(minValue, maxValue));\n var normalizedValue = minValue < maxValue\n ? (clampedValue - minValue) / (maxValue - minValue)\n : (minValue - clampedValue) / (minValue - maxValue);\n var offset = normalizedValue * initial;\n return offset;\n}\n\nvar valueSet = element.remember('valueSet');\nif (ctx.properties.progressBar) {\n var initial = ctx.properties.valueBox ? 135: 168;\n if (!valueSet) {\n element.remember('valueSet', true);\n element.attr({height: 2});\n }\n \n var minValue = ctx.properties.minValue;\n var maxValue = ctx.properties.maxValue;\n var value = ctx.values.value;\n \n var colorProcessor = ctx.properties.progressBarColor;\n colorProcessor.update(value);\n var fill = colorProcessor.color;\n element.attr({fill: fill, stroke: fill});\n ctx.tags.progressCircle[0].fill(fill);\n \n var height = calculateOffset(value, minValue, maxValue, initial);\n\n var elementHeight = element.remember('height');\n if (height !== elementHeight) {\n element.remember('height', height);\n ctx.api.cssAnimate(element, 500).attr({height: height+2});\n }\n} else {\n if (valueSet) {\n element.remember('valueSet', false);\n }\n}", + "actions": null + }, + { + "tag": "scale", + "stateRenderFunction": "var scaleSet = element.remember('scaleSet');\nif (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n var minValue = ctx.properties.minValue;\n var maxValue = ctx.properties.maxValue;\n \n var start = 11;\n var end = ctx.properties.valueBox ? 134 : 167;\n var majorIntervalLength = end / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n element.add(ctx.svg.line(50, end+11, 50, 11).stroke({ width: 1 }).attr({class: 'majorTick'}));\n for (var i = 0; i < majorIntervals+1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(38, y, 50, y).stroke({ width: 1 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (maxValue - ((maxValue - (minValue)) / (majorIntervals) * i)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 32, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n}\n\nvar majorFont = ctx.properties.majorFont;\nvar majorColor = ctx.properties.majorColor;\nvar minorColor = ctx.properties.minorColor;\nif (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n} else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n}\n\nvar majorTicks = element.find('line.majorTick');\nmajorTicks.forEach(t => t.attr({stroke: majorColor}));\n\nvar majorTicksText = element.find('text.majorTickText');\nctx.api.font(majorTicksText, majorFont, majorColor);\n\nvar minorTicks = element.find('line.minorTick');\nminorTicks.forEach(t => t.attr({stroke: minorColor}));\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(44, minorY, 50, minorY).stroke({ width: 1 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "actions": null + }, + { + "tag": "value-box", + "stateRenderFunction": "if (ctx.properties.valueBox) {\n element.show();\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "value-box-background", + "stateRenderFunction": "if (ctx.properties.valueBox) {\n var colorProcessor = ctx.properties.valueBoxColor;\n colorProcessor.update(ctx.values.value);\n element.attr({fill: colorProcessor.color});\n}", + "actions": null + }, + { + "tag": "value-text", + "stateRenderFunction": "if (ctx.properties.valueBox) {\n var valueTextFont = ctx.properties.valueTextFont;\n var valueTextColor = ctx.properties.valueTextColor;\n var currentVolume = ctx.values.value;\n var valueText = ctx.api.formatValue(currentVolume, 0, ctx.properties.valueUnits, false);\n var colorProcessor = ctx.properties.valueTextColor;\n colorProcessor.update(ctx.values.value);\n ctx.api.font(element, valueTextFont, colorProcessor.color);\n ctx.api.text(element, valueText);\n}", + "actions": null + } + ], + "behavior": [ + { + "id": "value", + "name": "{i18n:scada.symbol.value}", + "hint": null, + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "GET_TIME_SERIES", + "defaultValue": null, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "waterLevel" + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning-state}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical-state}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.critical-state-animation}", + "hint": "{i18n:scada.symbol.critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "minValue", + "name": "{i18n:scada.symbol.min-max-value}", + "type": "number", + "default": 0, + "required": true, + "subLabel": "{i18n:scada.symbol.min-value}", + "divider": true, + "min": -1000, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "maxValue", + "name": "{i18n:scada.symbol.min-max-value}", + "type": "number", + "default": 100, + "required": true, + "subLabel": "{i18n:scada.symbol.max-value}", + "max": 1000, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "backgroundColor", + "name": "{i18n:scada.symbol.background-color}", + "type": "color", + "default": "#FFFFFF", + "disabled": false, + "visible": true + }, + { + "id": "progressBar", + "name": "{i18n:scada.symbol.progress-bar}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "progressBarColor", + "name": "{i18n:scada.symbol.progress-bar}", + "type": "color_settings", + "default": { + "type": "constant", + "color": "#4D94E1", + "gradient": { + "advancedMode": false, + "gradient": [ + "rgba(0, 255, 0, 1)", + "rgba(255, 0, 0, 1)" + ], + "gradientAdvanced": [ + { + "source": { + "type": "constant" + }, + "color": "rgba(0, 255, 0, 1)" + }, + { + "source": { + "type": "constant" + }, + "color": "rgba(255, 0, 0, 1)" + } + ], + "minValue": 0, + "maxValue": 100 + }, + "rangeList": { + "advancedMode": false, + "range": [], + "rangeAdvanced": [] + }, + "colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';" + }, + "disableOnProperty": "progressBar", + "disabled": false, + "visible": true + }, + { + "id": "progressArrow", + "name": "{i18n:scada.symbol.progress-arrow}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, + { + "id": "progressArrowColor", + "name": "{i18n:scada.symbol.progress-arrow}", + "type": "color_settings", + "default": { + "type": "constant", + "color": "#1C943E", + "gradient": { + "advancedMode": false, + "gradient": [ + "rgba(0, 255, 0, 1)", + "rgba(255, 0, 0, 1)" + ], + "gradientAdvanced": [ + { + "source": { + "type": "constant" + }, + "color": "rgba(0, 255, 0, 1)" + }, + { + "source": { + "type": "constant" + }, + "color": "rgba(255, 0, 0, 1)" + } + ], + "minValue": 0, + "maxValue": 100 + }, + "rangeList": { + "advancedMode": false, + "range": [], + "rangeAdvanced": [] + }, + "colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';" + }, + "disableOnProperty": "progressArrow", + "disabled": true, + "visible": true + }, + { + "id": "valueBox", + "name": "{i18n:scada.symbol.value-box}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, + { + "id": "valueBoxColor", + "name": "{i18n:scada.symbol.value-box}", + "type": "color_settings", + "default": { + "type": "constant", + "color": "#F3F3F3", + "gradient": { + "advancedMode": false, + "gradient": [ + "rgba(0, 255, 0, 1)", + "rgba(255, 0, 0, 1)" + ], + "gradientAdvanced": [ + { + "source": { + "type": "constant" + }, + "color": "rgba(0, 255, 0, 1)" + }, + { + "source": { + "type": "constant" + }, + "color": "rgba(255, 0, 0, 1)" + } + ], + "minValue": 0, + "maxValue": 100 + }, + "rangeList": { + "advancedMode": false, + "range": [], + "rangeAdvanced": [] + }, + "colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';" + }, + "disableOnProperty": "valueBox", + "disabled": false, + "visible": true + }, + { + "id": "valueUnits", + "name": "{i18n:scada.symbol.value-text}", + "type": "units", + "default": "%", + "subLabel": "{i18n:scada.symbol.units}", + "disableOnProperty": "valueBox", + "disabled": false, + "visible": true + }, + { + "id": "valueTextFont", + "name": "{i18n:scada.symbol.value-text}", + "type": "font", + "default": { + "size": 14, + "sizeUnit": "px", + "family": "Roboto", + "weight": "500", + "style": "normal" + }, + "disableOnProperty": "valueBox", + "disabled": false, + "visible": true + }, + { + "id": "valueTextColor", + "name": "{i18n:scada.symbol.value-text}", + "type": "color_settings", + "default": { + "type": "constant", + "color": "#0000008A", + "gradient": { + "advancedMode": false, + "gradient": [ + "rgba(0, 255, 0, 1)", + "rgba(255, 0, 0, 1)" + ], + "gradientAdvanced": [ + { + "source": { + "type": "constant" + }, + "color": "rgba(0, 255, 0, 1)" + }, + { + "source": { + "type": "constant" + }, + "color": "rgba(255, 0, 0, 1)" + } + ], + "minValue": 0, + "maxValue": 100 + }, + "rangeList": { + "advancedMode": false, + "range": [], + "rangeAdvanced": [] + }, + "colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';" + }, + "disableOnProperty": "valueBox", + "disabled": false, + "visible": true + }, + { + "id": "majorIntervals", + "name": "{i18n:scada.symbol.major-ticks}", + "type": "number", + "default": 5, + "subLabel": "{i18n:scada.symbol.intervals}", + "divider": true, + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "majorFont", + "name": "{i18n:scada.symbol.major-ticks}", + "type": "font", + "default": { + "size": 12, + "sizeUnit": "px", + "family": "Roboto", + "weight": "500", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "majorColor", + "name": "{i18n:scada.symbol.major-ticks-color}", + "type": "color", + "default": "#747474", + "subLabel": "{i18n:scada.symbol.normal}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "majorWarningColor", + "name": "{i18n:scada.symbol.major-ticks-color}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "majorCriticalColor", + "name": "{i18n:scada.symbol.major-ticks-color}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + }, + { + "id": "minorIntervals", + "name": "{i18n:scada.symbol.minor-ticks}", + "type": "number", + "default": 10, + "subLabel": "{i18n:scada.symbol.intervals}", + "min": 1, + "disabled": false, + "visible": true + }, + { + "id": "minorColor", + "name": "{i18n:scada.symbol.minor-ticks-color}", + "type": "color", + "default": "#747474", + "subLabel": "{i18n:scada.symbol.normal}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "minorWarningColor", + "name": "{i18n:scada.symbol.minor-ticks-color}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "minorCriticalColor", + "name": "{i18n:scada.symbol.minor-ticks-color}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + } + ] +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 37% + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/small-meter.svg b/application/src/main/data/json/system/scada_symbols/small-meter.svg new file mode 100644 index 0000000000..551c8d520a --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/small-meter.svg @@ -0,0 +1,688 @@ +{ + "title": "Small meter", + "description": "Small meter displays the current value with a moving pointer on the scale.", + "searchTags": [ + "scale", + "level", + "progress", + "thermometer" + ], + "widgetSizeX": 1, + "widgetSizeY": 2, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "element.attr({fill: ctx.properties.backgroundColor});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "progress-indicator", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue, initial) {\n var clampedValue = Math.max(Math.min(value, Math.max(minValue, maxValue)), Math.min(minValue, maxValue));\n var normalizedValue = minValue < maxValue\n ? (clampedValue - minValue) / (maxValue - minValue)\n : (minValue - clampedValue) / (minValue - maxValue);\n var offset = initial - (normalizedValue * initial);\n return offset;\n}\n\nvar valueSet = element.remember('valueSet');\n\nif (ctx.properties.progressArrow && !ctx.properties.progressBar) {\n element.show();\n var initial = ctx.properties.valueBox ? 132 : 167;\n if (!valueSet) {\n element.remember('valueSet', true);\n element.transform({\n translateY: initial\n });\n }\n \n var minValue = ctx.properties.minValue;\n var maxValue = ctx.properties.maxValue;\n var value = ctx.values.value;\n \n var colorProcessor = ctx.properties.progressArrowColor;\n colorProcessor.update(value);\n var fill = colorProcessor.color;\n element.attr({fill: fill});\n \n var offset = calculateOffset(value, minValue, maxValue, initial);\n\n var elementOffset = element.remember('offset');\n if (offset !== elementOffset) {\n element.remember('offset', offset);\n ctx.api.cssAnimate(element, 500).transform({\n translateY: offset\n });\n }\n} else {\n element.hide();\n if (valueSet) {\n element.remember('valueSet', false);\n }\n}", + "actions": null + }, + { + "tag": "progressBar", + "stateRenderFunction": "if (ctx.properties.progressBar) {\n element.show();\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "progressBorder", + "stateRenderFunction": "if (!ctx.properties.valueBox) {\n element.attr({'height': 168})\n}", + "actions": null + }, + { + "tag": "progressCircle", + "stateRenderFunction": "if (!ctx.properties.valueBox) {\n element.attr({cy:185});\n}", + "actions": null + }, + { + "tag": "progressFill", + "stateRenderFunction": "if (!ctx.properties.valueBox) {\n element.attr({y:-180});\n}\n\nfunction calculateOffset(value, minValue, maxValue, initial) {\n var clampedValue = Math.max(Math.min(value, Math.max(minValue, maxValue)), Math.min(minValue, maxValue));\n var normalizedValue = minValue < maxValue\n ? (clampedValue - minValue) / (maxValue - minValue)\n : (minValue - clampedValue) / (minValue - maxValue);\n var offset = normalizedValue * initial;\n return offset;\n}\n\nvar valueSet = element.remember('valueSet');\nif (ctx.properties.progressBar) {\n var initial = ctx.properties.valueBox ? 132: 168;\n if (!valueSet) {\n element.remember('valueSet', true);\n element.attr({height: 2});\n }\n \n var minValue = ctx.properties.minValue;\n var maxValue = ctx.properties.maxValue;\n var value = ctx.values.value;\n \n var colorProcessor = ctx.properties.progressBarColor;\n colorProcessor.update(value);\n var fill = colorProcessor.color;\n element.attr({fill: fill, stroke: fill});\n ctx.tags.progressCircle[0].fill(fill);\n \n var height = calculateOffset(value, minValue, maxValue, initial);\n\n var elementHeight = element.remember('height');\n if (height !== elementHeight) {\n element.remember('height', height);\n ctx.api.cssAnimate(element, 500).attr({height: height+2});\n }\n} else {\n if (valueSet) {\n element.remember('valueSet', false);\n }\n}", + "actions": null + }, + { + "tag": "scale", + "stateRenderFunction": "var scaleSet = element.remember('scaleSet');\nif (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n var minValue = ctx.properties.minValue;\n var maxValue = ctx.properties.maxValue;\n \n var start = 11;\n var end = ctx.properties.valueBox ? 132 : 167;\n var majorIntervalLength = end / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n element.add(ctx.svg.line(63, end+11, 63, 11).stroke({ width: 1 }).attr({class: 'majorTick'}));\n for (var i = 0; i < majorIntervals+1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(51, y, 63, y).stroke({ width: 1 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (maxValue - ((maxValue - (minValue)) / (majorIntervals) * i)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 45, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n}\n\nvar majorFont = ctx.properties.majorFont;\nvar majorColor = ctx.properties.majorColor;\nvar minorColor = ctx.properties.minorColor;\nif (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n} else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n}\n\nvar majorTicks = element.find('line.majorTick');\nmajorTicks.forEach(t => t.attr({stroke: majorColor}));\n\nvar majorTicksText = element.find('text.majorTickText');\nctx.api.font(majorTicksText, majorFont, majorColor);\n\nvar minorTicks = element.find('line.minorTick');\nminorTicks.forEach(t => t.attr({stroke: minorColor}));\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(57, minorY, 63, minorY).stroke({ width: 1 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "actions": null + }, + { + "tag": "value-box", + "stateRenderFunction": "if (ctx.properties.valueBox) {\n element.show();\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "value-box-background", + "stateRenderFunction": "if (ctx.properties.valueBox) {\n var colorProcessor = ctx.properties.valueBoxColor;\n colorProcessor.update(ctx.values.value);\n element.attr({fill: colorProcessor.color});\n}", + "actions": null + }, + { + "tag": "value-text", + "stateRenderFunction": "if (ctx.properties.valueBox) {\n var valueTextFont = ctx.properties.valueTextFont;\n var valueTextColor = ctx.properties.valueTextColor;\n var currentVolume = ctx.values.value;\n var valueText = ctx.api.formatValue(currentVolume, 0, ctx.properties.valueUnits, false);\n var colorProcessor = ctx.properties.valueTextColor;\n colorProcessor.update(ctx.values.value);\n ctx.api.font(element, valueTextFont, colorProcessor.color);\n ctx.api.text(element, valueText);\n}", + "actions": null + } + ], + "behavior": [ + { + "id": "value", + "name": "{i18n:scada.symbol.value}", + "hint": null, + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "GET_TIME_SERIES", + "defaultValue": null, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "waterLevel" + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning-state}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical-state}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.critical-state-animation}", + "hint": "{i18n:scada.symbol.critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "minValue", + "name": "{i18n:scada.symbol.min-max-value}", + "type": "number", + "default": 0, + "required": true, + "subLabel": "{i18n:scada.symbol.min-value}", + "divider": true, + "min": -1000, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "maxValue", + "name": "{i18n:scada.symbol.min-max-value}", + "type": "number", + "default": 100, + "required": true, + "subLabel": "{i18n:scada.symbol.max-value}", + "max": 1000, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "backgroundColor", + "name": "{i18n:scada.symbol.background-color}", + "type": "color", + "default": "#FFFFFF", + "disabled": false, + "visible": true + }, + { + "id": "progressBar", + "name": "{i18n:scada.symbol.progress-bar}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "progressBarColor", + "name": "{i18n:scada.symbol.progress-bar}", + "type": "color_settings", + "default": { + "type": "constant", + "color": "#4D94E1", + "gradient": { + "advancedMode": false, + "gradient": [ + "rgba(0, 255, 0, 1)", + "rgba(255, 0, 0, 1)" + ], + "gradientAdvanced": [ + { + "source": { + "type": "constant" + }, + "color": "rgba(0, 255, 0, 1)" + }, + { + "source": { + "type": "constant" + }, + "color": "rgba(255, 0, 0, 1)" + } + ], + "minValue": 0, + "maxValue": 100 + }, + "rangeList": { + "advancedMode": false, + "range": [], + "rangeAdvanced": [] + }, + "colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';" + }, + "disableOnProperty": "progressBar", + "disabled": false, + "visible": true + }, + { + "id": "progressArrow", + "name": "{i18n:scada.symbol.progress-arrow}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, + { + "id": "progressArrowColor", + "name": "{i18n:scada.symbol.progress-arrow}", + "type": "color_settings", + "default": { + "type": "constant", + "color": "#1C943E", + "gradient": { + "advancedMode": false, + "gradient": [ + "rgba(0, 255, 0, 1)", + "rgba(255, 0, 0, 1)" + ], + "gradientAdvanced": [ + { + "source": { + "type": "constant" + }, + "color": "rgba(0, 255, 0, 1)" + }, + { + "source": { + "type": "constant" + }, + "color": "rgba(255, 0, 0, 1)" + } + ], + "minValue": 0, + "maxValue": 100 + }, + "rangeList": { + "advancedMode": false, + "range": [], + "rangeAdvanced": [] + }, + "colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';" + }, + "disableOnProperty": "progressArrow", + "disabled": true, + "visible": true + }, + { + "id": "valueBox", + "name": "{i18n:scada.symbol.value-box}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "valueBoxColor", + "name": "{i18n:scada.symbol.value-box}", + "type": "color_settings", + "default": { + "type": "constant", + "color": "#F3F3F3", + "gradient": { + "advancedMode": false, + "gradient": [ + "rgba(0, 255, 0, 1)", + "rgba(255, 0, 0, 1)" + ], + "gradientAdvanced": [ + { + "source": { + "type": "constant" + }, + "color": "rgba(0, 255, 0, 1)" + }, + { + "source": { + "type": "constant" + }, + "color": "rgba(255, 0, 0, 1)" + } + ], + "minValue": 0, + "maxValue": 100 + }, + "rangeList": { + "advancedMode": false, + "range": [], + "rangeAdvanced": [] + }, + "colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';" + }, + "disableOnProperty": "valueBox", + "disabled": true, + "visible": true + }, + { + "id": "valueUnits", + "name": "{i18n:scada.symbol.value-text}", + "type": "units", + "default": "%", + "subLabel": "{i18n:scada.symbol.units}", + "disableOnProperty": "valueBox", + "disabled": true, + "visible": true + }, + { + "id": "valueTextFont", + "name": "{i18n:scada.symbol.value-text}", + "type": "font", + "default": { + "size": 14, + "sizeUnit": "px", + "family": "Roboto", + "weight": "500", + "style": "normal" + }, + "disableOnProperty": "valueBox", + "disabled": true, + "visible": true + }, + { + "id": "valueTextColor", + "name": "{i18n:scada.symbol.value-text}", + "type": "color_settings", + "default": { + "type": "constant", + "color": "#0000008A", + "gradient": { + "advancedMode": false, + "gradient": [ + "rgba(0, 255, 0, 1)", + "rgba(255, 0, 0, 1)" + ], + "gradientAdvanced": [ + { + "source": { + "type": "constant" + }, + "color": "rgba(0, 255, 0, 1)" + }, + { + "source": { + "type": "constant" + }, + "color": "rgba(255, 0, 0, 1)" + } + ], + "minValue": 0, + "maxValue": 100 + }, + "rangeList": { + "advancedMode": false, + "range": [], + "rangeAdvanced": [] + }, + "colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';" + }, + "disableOnProperty": "valueBox", + "disabled": true, + "visible": true + }, + { + "id": "majorIntervals", + "name": "{i18n:scada.symbol.major-ticks}", + "type": "number", + "default": 5, + "subLabel": "{i18n:scada.symbol.intervals}", + "divider": true, + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "majorFont", + "name": "{i18n:scada.symbol.major-ticks}", + "type": "font", + "default": { + "size": 12, + "sizeUnit": "px", + "family": "Roboto", + "weight": "500", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "majorColor", + "name": "{i18n:scada.symbol.major-ticks-color}", + "type": "color", + "default": "#747474", + "subLabel": "{i18n:scada.symbol.normal}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "majorWarningColor", + "name": "{i18n:scada.symbol.major-ticks-color}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "majorCriticalColor", + "name": "{i18n:scada.symbol.major-ticks-color}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + }, + { + "id": "minorIntervals", + "name": "{i18n:scada.symbol.minor-ticks}", + "type": "number", + "default": 10, + "subLabel": "{i18n:scada.symbol.intervals}", + "min": 1, + "disabled": false, + "visible": true + }, + { + "id": "minorColor", + "name": "{i18n:scada.symbol.minor-ticks-color}", + "type": "color", + "default": "#747474", + "subLabel": "{i18n:scada.symbol.normal}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "minorWarningColor", + "name": "{i18n:scada.symbol.minor-ticks-color}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "minorCriticalColor", + "name": "{i18n:scada.symbol.minor-ticks-color}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + } + ] +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 37% + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/small-right-center.svg b/application/src/main/data/json/system/scada_symbols/small-right-center.svg new file mode 100644 index 0000000000..de1bee5acd --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/small-right-center.svg @@ -0,0 +1,717 @@ +{ + "title": "Small right meter", + "description": "Small right meter displays the current value with a moving pointer on the scale.", + "searchTags": [ + "scale", + "level", + "progress", + "thermometer" + ], + "widgetSizeX": 1, + "widgetSizeY": 2, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "element.attr({fill: ctx.properties.backgroundColor});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "progress-indicator", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue, initial) {\n var clampedValue = Math.max(Math.min(value, Math.max(minValue, maxValue)), Math.min(minValue, maxValue));\n var normalizedValue = minValue < maxValue\n ? (clampedValue - minValue) / (maxValue - minValue)\n : (minValue - clampedValue) / (minValue - maxValue);\n var offset = initial - (normalizedValue * initial);\n return offset;\n}\n\nvar valueSet = element.remember('valueSet');\n\nif (ctx.properties.progressArrow && !ctx.properties.progressBar) {\n element.show();\n var initial = ctx.properties.valueBox ? 135 : 168;\n if (!valueSet) {\n element.remember('valueSet', true);\n element.transform({\n translateY: initial\n });\n }\n \n var minValue = ctx.properties.minValue;\n var maxValue = ctx.properties.maxValue;\n var value = ctx.values.value;\n \n var colorProcessor = ctx.properties.progressArrowColor;\n colorProcessor.update(value);\n var fill = colorProcessor.color;\n element.attr({fill: fill});\n \n var offset = calculateOffset(value, minValue, maxValue, initial);\n\n var elementOffset = element.remember('offset');\n if (offset !== elementOffset) {\n element.remember('offset', offset);\n ctx.api.cssAnimate(element, 500).transform({\n translateY: offset\n });\n }\n} else {\n element.hide();\n if (valueSet) {\n element.remember('valueSet', false);\n }\n}", + "actions": null + }, + { + "tag": "progressBar", + "stateRenderFunction": "if (ctx.properties.progressBar) {\n element.show();\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "progressBorder", + "stateRenderFunction": "if (ctx.properties.valueBox) {\n element.attr({'height': 137})\n}", + "actions": null + }, + { + "tag": "progressCircle", + "stateRenderFunction": "if (ctx.properties.valueBox) {\n element.attr({cy:152});\n}", + "actions": null + }, + { + "tag": "progressFill", + "stateRenderFunction": "if (ctx.properties.valueBox) {\n element.attr({y:-147});\n}\n\nfunction calculateOffset(value, minValue, maxValue, initial) {\n var clampedValue = Math.max(Math.min(value, Math.max(minValue, maxValue)), Math.min(minValue, maxValue));\n var normalizedValue = minValue < maxValue\n ? (clampedValue - minValue) / (maxValue - minValue)\n : (minValue - clampedValue) / (minValue - maxValue);\n var offset = normalizedValue * initial;\n return offset;\n}\n\nvar valueSet = element.remember('valueSet');\nif (ctx.properties.progressBar) {\n var initial = ctx.properties.valueBox ? 135: 168;\n if (!valueSet) {\n element.remember('valueSet', true);\n element.attr({height: 2});\n }\n \n var minValue = ctx.properties.minValue;\n var maxValue = ctx.properties.maxValue;\n var value = ctx.values.value;\n \n var colorProcessor = ctx.properties.progressBarColor;\n colorProcessor.update(value);\n var fill = colorProcessor.color;\n element.attr({fill: fill, stroke: fill});\n ctx.tags.progressCircle[0].fill(fill);\n \n var height = calculateOffset(value, minValue, maxValue, initial);\n\n var elementHeight = element.remember('height');\n if (height !== elementHeight) {\n element.remember('height', height);\n ctx.api.cssAnimate(element, 500).attr({height: height+2});\n }\n} else {\n if (valueSet) {\n element.remember('valueSet', false);\n }\n}", + "actions": null + }, + { + "tag": "scale", + "stateRenderFunction": "var scaleSet = element.remember('scaleSet');\nif (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n var minValue = ctx.properties.minValue;\n var maxValue = ctx.properties.maxValue;\n \n var start = 11;\n var end = ctx.properties.valueBox ? 134 : 167;\n var majorIntervalLength = end / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n element.add(ctx.svg.line(73, end+11, 73, 11).stroke({ width: 1 }).attr({class: 'majorTick'}));\n for (var i = 0; i < majorIntervals+1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(61, y, 73, y).stroke({ width: 1 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (maxValue - ((maxValue - (minValue)) / (majorIntervals) * i)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 55, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n}\n\nvar majorFont = ctx.properties.majorFont;\nvar majorColor = ctx.properties.majorColor;\nvar minorColor = ctx.properties.minorColor;\nif (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n} else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n}\n\nvar majorTicks = element.find('line.majorTick');\nmajorTicks.forEach(t => t.attr({stroke: majorColor}));\n\nvar majorTicksText = element.find('text.majorTickText');\nctx.api.font(majorTicksText, majorFont, majorColor);\n\nvar minorTicks = element.find('line.minorTick');\nminorTicks.forEach(t => t.attr({stroke: minorColor}));\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(67, minorY, 73, minorY).stroke({ width: 1 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "actions": null + }, + { + "tag": "value-box", + "stateRenderFunction": "if (ctx.properties.valueBox) {\n element.show();\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "value-box-background", + "stateRenderFunction": "if (ctx.properties.valueBox) {\n var colorProcessor = ctx.properties.valueBoxColor;\n colorProcessor.update(ctx.values.value);\n element.attr({fill: colorProcessor.color});\n}", + "actions": null + }, + { + "tag": "value-text", + "stateRenderFunction": "if (ctx.properties.valueBox) {\n var valueTextFont = ctx.properties.valueTextFont;\n var valueTextColor = ctx.properties.valueTextColor;\n var currentVolume = ctx.values.value;\n var valueText = ctx.api.formatValue(currentVolume, 0, ctx.properties.valueUnits, false);\n var colorProcessor = ctx.properties.valueTextColor;\n colorProcessor.update(ctx.values.value);\n ctx.api.font(element, valueTextFont, colorProcessor.color);\n ctx.api.text(element, valueText);\n}", + "actions": null + } + ], + "behavior": [ + { + "id": "value", + "name": "{i18n:scada.symbol.value}", + "hint": null, + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "GET_TIME_SERIES", + "defaultValue": null, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "waterLevel" + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning-state}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical-state}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.critical-state-animation}", + "hint": "{i18n:scada.symbol.critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "minValue", + "name": "{i18n:scada.symbol.min-max-value}", + "type": "number", + "default": 0, + "required": true, + "subLabel": "{i18n:scada.symbol.min-value}", + "divider": true, + "min": -1000, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "maxValue", + "name": "{i18n:scada.symbol.min-max-value}", + "type": "number", + "default": 100, + "required": true, + "subLabel": "{i18n:scada.symbol.max-value}", + "max": 1000, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "backgroundColor", + "name": "{i18n:scada.symbol.background-color}", + "type": "color", + "default": "#FFFFFF", + "disabled": false, + "visible": true + }, + { + "id": "progressBar", + "name": "{i18n:scada.symbol.progress-bar}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "progressBarColor", + "name": "{i18n:scada.symbol.progress-bar}", + "type": "color_settings", + "default": { + "type": "constant", + "color": "#4D94E1", + "gradient": { + "advancedMode": false, + "gradient": [ + "rgba(0, 255, 0, 1)", + "rgba(255, 0, 0, 1)" + ], + "gradientAdvanced": [ + { + "source": { + "type": "constant" + }, + "color": "rgba(0, 255, 0, 1)" + }, + { + "source": { + "type": "constant" + }, + "color": "rgba(255, 0, 0, 1)" + } + ], + "minValue": 0, + "maxValue": 100 + }, + "rangeList": { + "advancedMode": false, + "range": [], + "rangeAdvanced": [] + }, + "colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';" + }, + "disableOnProperty": "progressBar", + "disabled": false, + "visible": true + }, + { + "id": "progressArrow", + "name": "{i18n:scada.symbol.progress-arrow}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, + { + "id": "progressArrowColor", + "name": "{i18n:scada.symbol.progress-arrow}", + "type": "color_settings", + "default": { + "type": "constant", + "color": "#1C943E", + "gradient": { + "advancedMode": false, + "gradient": [ + "rgba(0, 255, 0, 1)", + "rgba(255, 0, 0, 1)" + ], + "gradientAdvanced": [ + { + "source": { + "type": "constant" + }, + "color": "rgba(0, 255, 0, 1)" + }, + { + "source": { + "type": "constant" + }, + "color": "rgba(255, 0, 0, 1)" + } + ], + "minValue": 0, + "maxValue": 100 + }, + "rangeList": { + "advancedMode": false, + "range": [], + "rangeAdvanced": [] + }, + "colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';" + }, + "disableOnProperty": "progressArrow", + "disabled": false, + "visible": true + }, + { + "id": "valueBox", + "name": "{i18n:scada.symbol.value-box}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, + { + "id": "valueBoxColor", + "name": "{i18n:scada.symbol.value-box}", + "type": "color_settings", + "default": { + "type": "constant", + "color": "#F3F3F3", + "gradient": { + "advancedMode": false, + "gradient": [ + "rgba(0, 255, 0, 1)", + "rgba(255, 0, 0, 1)" + ], + "gradientAdvanced": [ + { + "source": { + "type": "constant" + }, + "color": "rgba(0, 255, 0, 1)" + }, + { + "source": { + "type": "constant" + }, + "color": "rgba(255, 0, 0, 1)" + } + ], + "minValue": 0, + "maxValue": 100 + }, + "rangeList": { + "advancedMode": false, + "range": [], + "rangeAdvanced": [] + }, + "colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';" + }, + "disableOnProperty": "valueBox", + "disabled": true, + "visible": true + }, + { + "id": "valueUnits", + "name": "{i18n:scada.symbol.value-text}", + "type": "units", + "default": "%", + "subLabel": "{i18n:scada.symbol.units}", + "disableOnProperty": "valueBox", + "disabled": true, + "visible": true + }, + { + "id": "valueTextFont", + "name": "{i18n:scada.symbol.value-text}", + "type": "font", + "default": { + "size": 14, + "sizeUnit": "px", + "family": "Roboto", + "weight": "500", + "style": "normal" + }, + "disableOnProperty": "valueBox", + "disabled": true, + "visible": true + }, + { + "id": "valueTextColor", + "name": "{i18n:scada.symbol.value-text}", + "type": "color_settings", + "default": { + "type": "constant", + "color": "#0000008A", + "gradient": { + "advancedMode": false, + "gradient": [ + "rgba(0, 255, 0, 1)", + "rgba(255, 0, 0, 1)" + ], + "gradientAdvanced": [ + { + "source": { + "type": "constant" + }, + "color": "rgba(0, 255, 0, 1)" + }, + { + "source": { + "type": "constant" + }, + "color": "rgba(255, 0, 0, 1)" + } + ], + "minValue": 0, + "maxValue": 100 + }, + "rangeList": { + "advancedMode": false, + "range": [], + "rangeAdvanced": [] + }, + "colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';" + }, + "disableOnProperty": "valueBox", + "disabled": true, + "visible": true + }, + { + "id": "majorIntervals", + "name": "{i18n:scada.symbol.major-ticks}", + "type": "number", + "default": 5, + "subLabel": "{i18n:scada.symbol.intervals}", + "divider": true, + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "majorFont", + "name": "{i18n:scada.symbol.major-ticks}", + "type": "font", + "default": { + "size": 12, + "sizeUnit": "px", + "family": "Roboto", + "weight": "500", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "majorColor", + "name": "{i18n:scada.symbol.major-ticks-color}", + "type": "color", + "default": "#747474", + "subLabel": "{i18n:scada.symbol.normal}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "majorWarningColor", + "name": "{i18n:scada.symbol.major-ticks-color}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "majorCriticalColor", + "name": "{i18n:scada.symbol.major-ticks-color}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + }, + { + "id": "minorIntervals", + "name": "{i18n:scada.symbol.minor-ticks}", + "type": "number", + "default": 10, + "subLabel": "{i18n:scada.symbol.intervals}", + "min": 1, + "disabled": false, + "visible": true + }, + { + "id": "minorColor", + "name": "{i18n:scada.symbol.minor-ticks-color}", + "type": "color", + "default": "#747474", + "subLabel": "{i18n:scada.symbol.normal}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "minorWarningColor", + "name": "{i18n:scada.symbol.minor-ticks-color}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "minorCriticalColor", + "name": "{i18n:scada.symbol.minor-ticks-color}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + } + ] +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 37% + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/scada_fluid_system.json b/application/src/main/data/json/system/widget_bundles/scada_fluid_system.json index 9aa3b299a8..e6f1e6a876 100644 --- a/application/src/main/data/json/system/widget_bundles/scada_fluid_system.json +++ b/application/src/main/data/json/system/widget_bundles/scada_fluid_system.json @@ -42,6 +42,10 @@ "vertical_inline_flow_meter", "left_analog_water_level_meter", "right_analog_water_level_meter", + "meter", + "small_meter", + "small_right_meter", + "small_left_meter", "leak_sensor", "centrifugal_pump", "small_right_motor_pump", diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index fc381fbb87..283ee42e5a 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3339,6 +3339,8 @@ "min-max-value": "Min and max value", "min-value": "Min", "max-value": "Max", + "progress-bar": "Progress bar", + "progress-arrow": "Progress arrow", "warning-scale-color": "Warning scale color", "critical-scale-color": "Critical scale color", "scale-color": "Scale color", From e1b29b500446943a2ca646bb56341c89cb8ba5d4 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 1 Jan 2025 14:28:38 +0100 Subject: [PATCH 034/108] removed custom reflection methods in favour ReflectionUtils --- .../service/ttl/AlarmsCleanUpService.java | 10 ++++-- .../server/controller/AbstractWebTest.java | 31 ------------------- .../service/ttl/AlarmsCleanUpServiceTest.java | 17 +++++----- 3 files changed, 17 insertions(+), 41 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/AlarmsCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/AlarmsCleanUpService.java index dd80eba6bc..75a459c283 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/AlarmsCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/AlarmsCleanUpService.java @@ -17,6 +17,7 @@ package org.thingsboard.server.service.ttl; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; @@ -66,7 +67,7 @@ public class AlarmsCleanUpService { try { cleanUp(tenantId); } catch (Exception e) { - log.warn("Failed to clean up alarms by ttl for tenant {}", tenantId, e); + getLogger().warn("Failed to clean up alarms by ttl for tenant {}", tenantId, e); } } } @@ -105,8 +106,13 @@ public class AlarmsCleanUpService { alarmService.delAlarmTypes(tenantId, typesToRemove); if (totalRemoved > 0) { - log.info("Removed {} outdated alarm(s) for tenant {} older than {}", totalRemoved, tenantId, new Date(expirationTime)); + getLogger().info("Removed {} outdated alarm(s) for tenant {} older than {}", totalRemoved, tenantId, new Date(expirationTime)); } } + // wrapper for tests to spy on static logger + Logger getLogger() { + return log; + } + } diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index aef4772053..5649caee5a 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -149,10 +149,6 @@ import org.thingsboard.server.service.security.auth.rest.LoginRequest; import org.thingsboard.server.service.security.model.token.JwtTokenFactory; import java.io.IOException; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.VarHandle; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; import java.nio.charset.StandardCharsets; import java.sql.SQLException; import java.util.ArrayList; @@ -1053,33 +1049,6 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { throw new AssertionError("Unexpected status " + mvcResult.getResponse().getStatus()); } - protected static T getFieldValue(Object target, String fieldName) throws Exception { - Field field = target.getClass().getDeclaredField(fieldName); - field.setAccessible(true); - return (T) field.get(target); - } - - protected static void setStaticFieldValue(Class targetCls, String fieldName, Object value) throws Exception { - Field field = targetCls.getDeclaredField(fieldName); - field.setAccessible(true); - field.set(null, value); - } - - protected static void setStaticFinalFieldValue(Class targetCls, String fieldName, Object value) throws Exception { - Field field = targetCls.getDeclaredField(fieldName); - field.setAccessible(true); - // Get the VarHandle for the 'modifiers' field in the Field class - MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup()); - VarHandle modifiersHandle = lookup.findVarHandle(Field.class, "modifiers", int.class); - - // Remove the final modifier from the field - int currentModifiers = field.getModifiers(); - modifiersHandle.set(field, currentModifiers & ~Modifier.FINAL); - - // Set the new value - field.set(null, value); - } - protected int getDeviceActorSubscriptionCount(DeviceId deviceId, FeatureType featureType) { DeviceActorMessageProcessor processor = getDeviceActorProcessor(deviceId); Map subscriptions = (Map) ReflectionTestUtils.getField(processor, getMapName(featureType)); diff --git a/application/src/test/java/org/thingsboard/server/service/ttl/AlarmsCleanUpServiceTest.java b/application/src/test/java/org/thingsboard/server/service/ttl/AlarmsCleanUpServiceTest.java index 7b9cb0ed4d..6206523d4d 100644 --- a/application/src/test/java/org/thingsboard/server/service/ttl/AlarmsCleanUpServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/ttl/AlarmsCleanUpServiceTest.java @@ -15,7 +15,7 @@ */ package org.thingsboard.server.service.ttl; -import org.junit.BeforeClass; +import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import org.slf4j.Logger; @@ -40,6 +40,7 @@ import java.util.concurrent.TimeUnit; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.startsWith; +import static org.mockito.BDDMockito.willReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -49,19 +50,19 @@ import static org.mockito.Mockito.verify; }) public class AlarmsCleanUpServiceTest extends AbstractControllerTest { - @Autowired + @SpyBean private AlarmsCleanUpService alarmsCleanUpService; @SpyBean private AlarmService alarmService; @Autowired private AlarmDao alarmDao; - private static Logger cleanUpServiceLogger; + private Logger cleanUpServiceLoggerSpy; - @BeforeClass - public static void before() throws Exception { - cleanUpServiceLogger = Mockito.spy(LoggerFactory.getLogger(AlarmsCleanUpService.class)); - setStaticFinalFieldValue(AlarmsCleanUpService.class, "log", cleanUpServiceLogger); + @Before + public void beforeEach() throws Exception { + cleanUpServiceLoggerSpy = Mockito.spy(LoggerFactory.getLogger(AlarmsCleanUpService.class)); + willReturn(cleanUpServiceLoggerSpy).given(alarmsCleanUpService).getLogger(); } @Test @@ -110,7 +111,7 @@ public class AlarmsCleanUpServiceTest extends AbstractControllerTest { verify(alarmService, never()).delAlarm(eq(tenantId), eq(freshAlarm), eq(false)); } - verify(cleanUpServiceLogger).info(startsWith("Removed {} outdated alarm"), eq((long) count), eq(tenantId), any()); + verify(cleanUpServiceLoggerSpy).info(startsWith("Removed {} outdated alarm"), eq((long) count), eq(tenantId), any()); } } From c5ca395844256dde207f2c1ec22139ecc339f06f Mon Sep 17 00:00:00 2001 From: Kulikov <44275303+nickAS21@users.noreply.github.com> Date: Fri, 3 Jan 2025 18:17:30 +0200 Subject: [PATCH 035/108] fix_bug_coaps_x509_docker_many_devices (#12327) * coaps: x509 - tests * coaps: x509 - tests add sever cert.pem * coaps: x509 - tests add sever cert.pem -2 * coaps: x509 - tests add sever cert.pem -3 * coaps: x509 - tests add sever cert.pem -4 * fix bug: coaps x509 - ddocker many devices with re-port * fix bug: coaps - add test, connect client with X509 * fix bug: coaps - add two test devices with the same port * fix bug: coaps - add two test devices with the same port (FeatureType.ATTRIBUTES) * fix bug: coaps - add two test devices with the same port (FeatureType.ATTRIBUTES) - 1 * fix bug: coap comments 1 --- .../coap/AbstractCoapIntegrationTest.java | 8 + .../client/CoapClientIntegrationTest.java | 2 - .../AbstractCoapSecurityIntegrationTest.java | 291 ++++++++++++++++++ ...pClientX509SecurityJksIntegrationTest.java | 44 +++ ...pClientX509SecurityPemIntegrationTest.java | 44 +++ .../CoapAttributesIntegrationTest.java | 3 - ...AbstractCoapTimeseriesIntegrationTest.java | 3 - .../transport/coap/x509/CertPrivateKey.java | 82 +++++ .../coap/x509/CoapClientX509Test.java | 239 ++++++++++++++ .../x509/TbAdvancedCertificateVerifier.java | 129 ++++++++ .../coap/credentials/client/cert.pem | 13 + .../coap/credentials/client/cert_01.pem | 14 + .../resources/coap/credentials/client/key.pem | 4 + .../coap/credentials/client/key_01.pem | 8 + .../coap/credentials/coapclientTest.jks | Bin 0 -> 20462 bytes .../coap/credentials/coapserverTest.jks | Bin 0 -> 1985 bytes .../coap/credentials/server/cert.pem | 35 +++ .../server/coapserver/CoapServerService.java | 3 +- .../coapserver/DefaultCoapServerService.java | 2 +- .../TbCoapDtlsCertificateVerifier.java | 5 +- .../TbCoapDtlsSessionInMemoryStorage.java | 9 +- .../coapserver/TbCoapDtlsSessionKey.java | 32 ++ .../transport/coap/CoapTransportResource.java | 44 ++- 23 files changed, 988 insertions(+), 26 deletions(-) create mode 100644 application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java create mode 100644 application/src/test/java/org/thingsboard/server/transport/coap/security/sql/CoapClientX509SecurityJksIntegrationTest.java create mode 100644 application/src/test/java/org/thingsboard/server/transport/coap/security/sql/CoapClientX509SecurityPemIntegrationTest.java create mode 100644 application/src/test/java/org/thingsboard/server/transport/coap/x509/CertPrivateKey.java create mode 100644 application/src/test/java/org/thingsboard/server/transport/coap/x509/CoapClientX509Test.java create mode 100644 application/src/test/java/org/thingsboard/server/transport/coap/x509/TbAdvancedCertificateVerifier.java create mode 100644 application/src/test/resources/coap/credentials/client/cert.pem create mode 100644 application/src/test/resources/coap/credentials/client/cert_01.pem create mode 100644 application/src/test/resources/coap/credentials/client/key.pem create mode 100644 application/src/test/resources/coap/credentials/client/key_01.pem create mode 100644 application/src/test/resources/coap/credentials/coapclientTest.jks create mode 100644 application/src/test/resources/coap/credentials/coapserverTest.jks create mode 100644 application/src/test/resources/coap/credentials/server/cert.pem create mode 100644 common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSessionKey.java diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java index 33c83b5623..2b0d87527b 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java @@ -54,6 +54,14 @@ public abstract class AbstractCoapIntegrationTest extends AbstractTransportInteg protected final byte[] EMPTY_PAYLOAD = new byte[0]; protected CoapTestClient client; + protected static final String PAYLOAD_VALUES_STR = "{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4," + + " \"key5\": {\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}}"; + protected static final String PAYLOAD_VALUES_STR_01 = "{\"key2\":\"value2\", \"key3\":false, \"key4\": 4.0, \"key5\": 5," + + " \"key6\": {\"someNumber_02\": 52, \"someArray_02\": [1,2,3,4], \"someNestedObject_02\": {\"key_02\": \"value_02\"}}}"; + + protected void processBeforeTest() throws Exception { + loginTenantAdmin(); + } protected void processAfterTest() throws Exception { if (client != null) { diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/client/CoapClientIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/client/CoapClientIntegrationTest.java index 112d3e6aa5..534c83bd40 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/client/CoapClientIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/client/CoapClientIntegrationTest.java @@ -63,8 +63,6 @@ import static org.thingsboard.server.common.data.query.EntityKeyType.SHARED_ATTR @DaoSqlTest public class CoapClientIntegrationTest extends AbstractCoapIntegrationTest { - private static final String PAYLOAD_VALUES_STR = "{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4," + - " \"key5\": {\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}}"; private static final List EXPECTED_KEYS = Arrays.asList("key1", "key2", "key3", "key4", "key5"); private static final String DEVICE_RESPONSE = "{\"value1\":\"A\",\"value2\":\"B\"}"; diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java new file mode 100644 index 0000000000..8413c6f32b --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java @@ -0,0 +1,291 @@ +/** + * 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. + */ +package org.thingsboard.server.transport.coap.security; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.core.CoapResponse; +import org.eclipse.californium.core.coap.CoAP; +import org.junit.Assert; +import org.springframework.test.context.TestPropertySource; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.CoapDeviceType; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.SaveDeviceWithCredentialsRequest; +import org.thingsboard.server.common.data.TransportPayloadType; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.common.data.security.DeviceCredentialsType; +import org.thingsboard.server.common.msg.session.FeatureType; +import org.thingsboard.server.transport.coap.AbstractCoapIntegrationTest; +import org.thingsboard.server.transport.coap.x509.CertPrivateKey; +import org.thingsboard.server.transport.coap.x509.CoapClientX509Test; +import org.thingsboard.server.transport.coap.CoapTestConfigProperties; + +import java.io.IOException; +import java.io.InputStream; +import java.net.ServerSocket; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import static org.awaitility.Awaitility.await; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@Slf4j +@TestPropertySource(properties = { + "coap.enabled=true", + "coap.dtls.enabled=true", + "coap.dtls.credentials.pem.cert_file=coap/credentials/server/cert.pem", + "device.connectivity.coaps.enabled=true", + "service.integrations.supported=ALL", + "transport.coap.enabled=true", +}) +public abstract class AbstractCoapSecurityIntegrationTest extends AbstractCoapIntegrationTest { + private static final String COAPS_BASE_URL = "coaps://localhost:5684/api/v1/"; + protected final String CREDENTIALS_PATH = "coap/credentials/"; + protected final String CREDENTIALS_PATH_CLIENT = CREDENTIALS_PATH + "client/"; + protected final String CREDENTIALS_PATH_CLIENT_CERT_PEM = CREDENTIALS_PATH_CLIENT + "cert.pem"; + protected final String CREDENTIALS_PATH_CLIENT_KEY_PEM = CREDENTIALS_PATH_CLIENT + "key.pem"; + protected final X509Certificate clientX509CertTrustNo; // client certificate signed by intermediate, rootCA with a good CN ("host name") + protected final PrivateKey clientPrivateKeyFromCertTrustNo; + + protected static final String CLIENT_JKS_FOR_TEST = "coapclientTest"; + protected static final String CLIENT_STORE_PWD = "client_ks_password"; + protected static final String CLIENT_ALIAS_CERT_TRUST_NO = "client_alias_trust_no"; + + protected AbstractCoapSecurityIntegrationTest() { + + try { + // Get certificates from key store + char[] clientKeyStorePwd = CLIENT_STORE_PWD.toCharArray(); + KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + try (InputStream clientKeyStoreFile = + this.getClass().getClassLoader(). + getResourceAsStream(CREDENTIALS_PATH + CLIENT_JKS_FOR_TEST + ".jks")) { + clientKeyStore.load(clientKeyStoreFile, clientKeyStorePwd); + } + // No trust + clientPrivateKeyFromCertTrustNo = (PrivateKey) clientKeyStore.getKey(CLIENT_ALIAS_CERT_TRUST_NO, clientKeyStorePwd); + clientX509CertTrustNo = (X509Certificate) clientKeyStore.getCertificate(CLIENT_ALIAS_CERT_TRUST_NO); + } catch (GeneralSecurityException | IOException e) { + throw new RuntimeException(e); + } + } + + protected Device createDeviceWithX509(String deviceName, DeviceProfileId deviceProfileId, X509Certificate clientX509Cert) throws Exception { + Device device = new Device(); + device.setName(deviceName); + device.setType(deviceName); + device.setDeviceProfileId(deviceProfileId); + + DeviceCredentials deviceCredentials = new DeviceCredentials(); + deviceCredentials.setCredentialsType(DeviceCredentialsType.X509_CERTIFICATE); + String pemFormatCert = CertPrivateKey.convertCertToPEM(clientX509Cert); + deviceCredentials.setCredentialsValue(pemFormatCert); + + SaveDeviceWithCredentialsRequest saveRequest = new SaveDeviceWithCredentialsRequest(device, deviceCredentials); + Device deviceX509 = readResponse(doPost("/api/device-with-credentials", saveRequest) + .andExpect(status().isOk()), Device.class); + DeviceCredentials savedDeviceCredentials = + doGet("/api/device/" + deviceX509.getId().getId() + "/credentials", DeviceCredentials.class); + Assert.assertNotNull(savedDeviceCredentials); + Assert.assertNotNull(savedDeviceCredentials.getId()); + Assert.assertEquals(deviceX509.getId(), savedDeviceCredentials.getDeviceId()); + Assert.assertEquals(DeviceCredentialsType.X509_CERTIFICATE, savedDeviceCredentials.getCredentialsType()); + accessToken = savedDeviceCredentials.getCredentialsId(); + assertNotNull(accessToken); + return deviceX509; + } + + protected void clientX509FromJksUpdateAttributesTest() throws Exception { + CertPrivateKey certPrivateKey = new CertPrivateKey(clientX509CertTrustNo, clientPrivateKeyFromCertTrustNo); + CoapTestConfigProperties configProperties = CoapTestConfigProperties.builder() + .coapDeviceType(CoapDeviceType.DEFAULT) + .transportPayloadType(TransportPayloadType.JSON) + .build(); + DeviceProfile deviceProfile = createCoapDeviceProfile(configProperties); + assertNotNull(deviceProfile); + CoapClientX509Test clientX509 = clientX509UpdateTest(FeatureType.ATTRIBUTES, certPrivateKey, + "CoapX509TrustNo_" + FeatureType.ATTRIBUTES.name(), deviceProfile.getId(), null); + clientX509.disconnect(); + } + + protected void clientX509FromPathUpdateFeatureTypeTest(FeatureType featureType) throws Exception { + CertPrivateKey certPrivateKey = new CertPrivateKey(CREDENTIALS_PATH_CLIENT_CERT_PEM, CREDENTIALS_PATH_CLIENT_KEY_PEM); + CoapTestConfigProperties configProperties = CoapTestConfigProperties.builder() + .coapDeviceType(CoapDeviceType.DEFAULT) + .transportPayloadType(TransportPayloadType.JSON) + .build(); + DeviceProfile deviceProfile = createCoapDeviceProfile(configProperties); + assertNotNull(deviceProfile); + CoapClientX509Test clientX509 = clientX509UpdateTest(featureType, certPrivateKey, + "CoapX509TrustNo_" + featureType.name(), deviceProfile.getId(), null); + clientX509.disconnect(); + } + protected void twoClientWithSamePortX509FromPathConnectTest() throws Exception { + CoapTestConfigProperties configProperties = CoapTestConfigProperties.builder() + .coapDeviceType(CoapDeviceType.DEFAULT) + .transportPayloadType(TransportPayloadType.JSON) + .build(); + DeviceProfile deviceProfile = createCoapDeviceProfile(configProperties); + CertPrivateKey certPrivateKey = new CertPrivateKey(CREDENTIALS_PATH_CLIENT_CERT_PEM, CREDENTIALS_PATH_CLIENT_KEY_PEM); + CertPrivateKey certPrivateKey_01 = new CertPrivateKey(CREDENTIALS_PATH_CLIENT + "cert_01.pem", + CREDENTIALS_PATH_CLIENT + "key_01.pem"); + Integer fixedPort = getFreePort(); + CoapClientX509Test clientX509 = clientX509UpdateTest(FeatureType.ATTRIBUTES, certPrivateKey, + "CoapX509TrustNo_" + FeatureType.TELEMETRY.name(), deviceProfile.getId(), fixedPort); + clientX509.disconnect(); + await("Need to make port " + fixedPort + " free") + .atMost(40, TimeUnit.SECONDS) + .until(() -> isPortAvailable(fixedPort)); + CoapClientX509Test clientX509_01 = clientX509UpdateTest(FeatureType.ATTRIBUTES, certPrivateKey_01, + "CoapX509TrustNo_" + FeatureType.TELEMETRY.name() + "_01", deviceProfile.getId(), + fixedPort, PAYLOAD_VALUES_STR_01); + clientX509_01.disconnect(); + } + + private CoapClientX509Test clientX509UpdateTest(FeatureType featureType, CertPrivateKey certPrivateKey, + String deviceName, DeviceProfileId deviceProfileId, Integer fixedPort) throws Exception { + return clientX509UpdateTest(featureType, certPrivateKey, deviceName, deviceProfileId, fixedPort, null); + } + + private CoapClientX509Test clientX509UpdateTest(FeatureType featureType, CertPrivateKey certPrivateKey, + String deviceName, DeviceProfileId deviceProfileId, Integer fixedPort, String payload) throws Exception { + String payloadValuesStr = payload == null ? PAYLOAD_VALUES_STR : payload; + Device deviceX509 = createDeviceWithX509(deviceName, deviceProfileId, certPrivateKey.getCert()); + CoapClientX509Test clientX509 = new CoapClientX509Test(certPrivateKey, featureType, COAPS_BASE_URL, fixedPort); + CoapResponse coapResponseX509 = clientX509.postMethod(payloadValuesStr); + assertNotNull(coapResponseX509); + assertEquals(CoAP.ResponseCode.CREATED, coapResponseX509.getCode()); + + if (FeatureType.ATTRIBUTES.equals(featureType)) { + DeviceId deviceId = deviceX509.getId(); + JsonNode expectedNode = JacksonUtil.toJsonNode(payloadValuesStr); + List expectedKeys = getKeysFromNode(expectedNode); + List actualKeys = getActualKeysList(deviceId, expectedKeys, "attributes/CLIENT_SCOPE"); + assertNotNull(actualKeys); + + Set actualKeySet = new HashSet<>(actualKeys); + Set expectedKeySet = new HashSet<>(expectedKeys); + assertEquals(expectedKeySet, actualKeySet); + + String getAttributesValuesUrl = getAttributesValuesUrl(deviceId, actualKeySet, "attributes/CLIENT_SCOPE"); + List> actualValues = doGetAsyncTyped(getAttributesValuesUrl, new TypeReference<>() { + }); + assertValuesList(actualValues, expectedNode); + } + return clientX509; + } + + private List getActualKeysList(DeviceId deviceId, List expectedKeys, String apiSuffix) throws Exception { + long start = System.currentTimeMillis(); + long end = System.currentTimeMillis() + 5000; + + List actualKeys = null; + while (start <= end) { + actualKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/" + apiSuffix, new TypeReference<>() { + }); + if (actualKeys.size() == expectedKeys.size()) { + break; + } + Thread.sleep(100); + start += 100; + } + return actualKeys; + } + + private String getAttributesValuesUrl(DeviceId deviceId, Set actualKeySet, String apiSuffix) { + return "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/" + apiSuffix + "?keys=" + String.join(",", actualKeySet); + } + + private List getKeysFromNode(JsonNode jNode) { + List jKeys = new ArrayList<>(); + Iterator fieldNames = jNode.fieldNames(); + while (fieldNames.hasNext()) { + jKeys.add(fieldNames.next()); + } + return jKeys; + } + + protected void assertValuesList(List> actualValues, JsonNode expectedValues) { + assertTrue(actualValues.size() > 0); + assertEquals(expectedValues.size(), actualValues.size()); + for (Map map : actualValues) { + String key = (String) map.get("key"); + Object actualValue = map.get("value"); + assertTrue(expectedValues.has(key)); + JsonNode expectedValue = expectedValues.get(key); + assertExpectedActualValue(expectedValue, actualValue); + } + } + + protected void assertExpectedActualValue(JsonNode expectedValue, Object actualValue) { + switch (expectedValue.getNodeType()) { + case STRING: + assertEquals(expectedValue.asText(), actualValue); + break; + case NUMBER: + if (expectedValue.isInt()) { + assertEquals(expectedValue.asInt(), actualValue); + } else if (expectedValue.isLong()) { + assertEquals(expectedValue.asLong(), actualValue); + } else if (expectedValue.isFloat() || expectedValue.isDouble()) { + assertEquals(expectedValue.asDouble(), actualValue); + } + break; + case BOOLEAN: + assertEquals(expectedValue.asBoolean(), actualValue); + break; + case ARRAY: + case OBJECT: + expectedValue.toString().equals(JacksonUtil.toString(actualValue)); + break; + default: + break; + } + } + + private static int getFreePort() throws IOException { + try (ServerSocket socket = new ServerSocket(0)) { + return socket.getLocalPort(); + } + } + + private static boolean isPortAvailable(int port) { + try (ServerSocket serverSocket = new ServerSocket(port)) { + serverSocket.setReuseAddress(true); + return true; + } catch (IOException e) { + return false; + } + } +} + diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/security/sql/CoapClientX509SecurityJksIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/security/sql/CoapClientX509SecurityJksIntegrationTest.java new file mode 100644 index 0000000000..12f373948b --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/security/sql/CoapClientX509SecurityJksIntegrationTest.java @@ -0,0 +1,44 @@ +/** + * 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. + */ +package org.thingsboard.server.transport.coap.security.sql; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Before; +import org.junit.Test; +import org.springframework.test.context.TestPropertySource; +import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.transport.coap.security.AbstractCoapSecurityIntegrationTest; + +@Slf4j +@DaoSqlTest +@TestPropertySource(properties = { + "coap.dtls.credentials.type=KEYSTORE", + "coap.dtls.credentials.keystore.store_file=coap/credentials/coapserverTest.jks", + "coap.dtls.credentials.keystore.key_password=server_ks_password", + "coap.dtls.credentials.keystore.key_alias=server", +}) +public class CoapClientX509SecurityJksIntegrationTest extends AbstractCoapSecurityIntegrationTest { + + @Before + public void beforeTest() throws Exception { + processBeforeTest(); + } + + @Test + public void testX509NoTrustFromJksConnectCoapSuccessUpdateAttributesSuccess() throws Exception { + clientX509FromJksUpdateAttributesTest(); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/security/sql/CoapClientX509SecurityPemIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/security/sql/CoapClientX509SecurityPemIntegrationTest.java new file mode 100644 index 0000000000..9e65943622 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/security/sql/CoapClientX509SecurityPemIntegrationTest.java @@ -0,0 +1,44 @@ +/** + * 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. + */ +package org.thingsboard.server.transport.coap.security.sql; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.common.msg.session.FeatureType; +import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.transport.coap.security.AbstractCoapSecurityIntegrationTest; + +@Slf4j +@DaoSqlTest +public class CoapClientX509SecurityPemIntegrationTest extends AbstractCoapSecurityIntegrationTest { + @Before + public void beforeTest() throws Exception { + processBeforeTest(); + } + + @Test + public void testX509NoTrustFromPathConnectCoapSuccessUpdateAttributesSuccess() throws Exception { + clientX509FromPathUpdateFeatureTypeTest(FeatureType.ATTRIBUTES); + } + @Test + public void testX509NoTrustFromPathConnectCoapSuccessUpdateTelemetrySuccess() throws Exception { + clientX509FromPathUpdateFeatureTypeTest(FeatureType.TELEMETRY); + } @Test + public void testTwoDevicesWithSamePortX509NoTrustFromPathConnectCoapSuccess() throws Exception { + twoClientWithSamePortX509FromPathConnectTest(); + } +} \ No newline at end of file diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/attributes/CoapAttributesIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/attributes/CoapAttributesIntegrationTest.java index 292fae3456..4c8b9094f0 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/attributes/CoapAttributesIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/attributes/CoapAttributesIntegrationTest.java @@ -44,9 +44,6 @@ import static org.junit.Assert.assertTrue; @DaoSqlTest public class CoapAttributesIntegrationTest extends AbstractCoapIntegrationTest { - private static final String PAYLOAD_VALUES_STR = "{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4," + - " \"key5\": {\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}}"; - @Before public void beforeTest() throws Exception { CoapTestConfigProperties configProperties = CoapTestConfigProperties.builder() diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/AbstractCoapTimeseriesIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/AbstractCoapTimeseriesIntegrationTest.java index b02a97bbfa..8cbff6c105 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/AbstractCoapTimeseriesIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/AbstractCoapTimeseriesIntegrationTest.java @@ -40,9 +40,6 @@ import static org.junit.Assert.assertNotNull; @Slf4j public abstract class AbstractCoapTimeseriesIntegrationTest extends AbstractCoapIntegrationTest { - private static final String PAYLOAD_VALUES_STR = "{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4," + - " \"key5\": {\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}}"; - @Before public void beforeTest() throws Exception { CoapTestConfigProperties configProperties = CoapTestConfigProperties.builder() diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/x509/CertPrivateKey.java b/application/src/test/java/org/thingsboard/server/transport/coap/x509/CertPrivateKey.java new file mode 100644 index 0000000000..8fce5b7e79 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/x509/CertPrivateKey.java @@ -0,0 +1,82 @@ +/** + * 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. + */ +package org.thingsboard.server.transport.coap.x509; + +import org.apache.commons.io.FileUtils; +import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; +import org.thingsboard.common.util.SslUtil; +import java.io.File; +import java.io.IOException; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.security.interfaces.ECPrivateKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; +import java.util.List; + +public class CertPrivateKey { + private final X509Certificate cert; + private PrivateKey privateKey; + + public CertPrivateKey(String certFilePathPem, String keyFilePathPem) throws Exception { + List certs = SslUtil.readCertFile(fileRead(certFilePathPem)); + this.cert = certs.get(0); + this.privateKey = SslUtil.readPrivateKey(fileRead(keyFilePathPem), null); + if (this.privateKey instanceof BCECPrivateKey) { + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(this.privateKey.getEncoded()); + KeyFactory keyFactory = KeyFactory.getInstance("EC"); + this.privateKey = keyFactory.generatePrivate(keySpec); + } + if (!(this.privateKey instanceof ECPrivateKey)) { + throw new RuntimeException("Private key generation must be of type java.security.interfaces.ECPrivateKey, which is used in the standard Java API!"); + } + } + + public CertPrivateKey(X509Certificate cert, PrivateKey privateKey) { + this.cert = cert; + this.privateKey = privateKey; + } + + public X509Certificate getCert() { + return this.cert; + } + + public PrivateKey getPrivateKey() { + return this.privateKey; + } + + private String fileRead(String fileName) throws IOException { + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource(fileName).getFile()); + return FileUtils.readFileToString(file, "UTF-8"); + } + + public static String convertCertToPEM(X509Certificate certificate) throws Exception { + StringBuilder pemBuilder = new StringBuilder(); + pemBuilder.append("-----BEGIN CERTIFICATE-----\n"); + // Copy cert to Base64 + String base64EncodedCert = Base64.getEncoder().encodeToString(certificate.getEncoded()); + int index = 0; + while (index < base64EncodedCert.length()) { + pemBuilder.append(base64EncodedCert, index, Math.min(index + 64, base64EncodedCert.length())); + pemBuilder.append("\n"); + index += 64; + } + pemBuilder.append("-----END CERTIFICATE-----\n"); + return pemBuilder.toString(); + } +} \ No newline at end of file diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/x509/CoapClientX509Test.java b/application/src/test/java/org/thingsboard/server/transport/coap/x509/CoapClientX509Test.java new file mode 100644 index 0000000000..d13380b19c --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/x509/CoapClientX509Test.java @@ -0,0 +1,239 @@ +/** + * 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. + */ +package org.thingsboard.server.transport.coap.x509; + +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.core.CoapClient; +import org.eclipse.californium.core.CoapHandler; +import org.eclipse.californium.core.CoapObserveRelation; +import org.eclipse.californium.core.CoapResponse; +import org.eclipse.californium.core.coap.CoAP; +import org.eclipse.californium.core.coap.MediaTypeRegistry; +import org.eclipse.californium.core.coap.Request; +import org.eclipse.californium.core.config.CoapConfig; +import org.eclipse.californium.core.network.CoapEndpoint; +import org.eclipse.californium.elements.config.Configuration; +import org.eclipse.californium.elements.config.ValueException; +import org.eclipse.californium.elements.exception.ConnectorException; +import org.eclipse.californium.scandium.DTLSConnector; +import org.eclipse.californium.scandium.config.DtlsConfig.SignatureAndHashAlgorithmsDefinition; +import org.eclipse.californium.scandium.config.DtlsConnectorConfig; +import org.eclipse.californium.scandium.dtls.CertificateType; +import org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm; +import org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.HashAlgorithm; +import org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.SignatureAlgorithm; +import org.eclipse.californium.scandium.dtls.cipher.CipherSuite; +import org.eclipse.californium.scandium.dtls.x509.CertificateProvider; +import org.eclipse.californium.scandium.dtls.x509.SingleCertificateProvider; +import org.thingsboard.server.common.msg.session.FeatureType; +import org.thingsboard.server.transport.coap.CoapTestCallback; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.eclipse.californium.core.config.CoapConfig.DEFAULT_BLOCKWISE_STATUS_LIFETIME_IN_SECONDS; +import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_AUTO_HANDSHAKE_TIMEOUT; +import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CIPHER_SUITES; +import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_MAX_FRAGMENTED_HANDSHAKE_MESSAGE_LENGTH; +import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_MAX_PENDING_HANDSHAKE_RESULT_JOBS; +import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_MAX_RETRANSMISSIONS; +import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_MAX_RETRANSMISSION_TIMEOUT; +import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RECEIVE_BUFFER_SIZE; +import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY; +import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RETRANSMISSION_TIMEOUT; +import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_ROLE; +import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_SIGNATURE_AND_HASH_ALGORITHMS; +import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_USE_HELLO_VERIFY_REQUEST; +import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_USE_MULTI_HANDSHAKE_MESSAGE_RECORDS; +import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_VERIFY_SERVER_CERTIFICATES_SUBJECT; +import static org.eclipse.californium.scandium.config.DtlsConfig.DtlsRole.CLIENT_ONLY; +import static org.eclipse.californium.scandium.config.DtlsConfig.MODULE; +import static org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.SHA256_WITH_ECDSA; +import static org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.SHA256_WITH_RSA; +import static org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.SHA384_WITH_ECDSA; + +@Slf4j +public class CoapClientX509Test { + + private static final long CLIENT_REQUEST_TIMEOUT = 60000L; + + private final CoapClient clientX509; + private final DTLSConnector dtlsConnector; + private final Configuration config; + private final CertPrivateKey certPrivateKey; + private final String coapsBaseUrl; + + @Getter + private CoAP.Type type = CoAP.Type.CON; + + public CoapClientX509Test(CertPrivateKey certPrivateKey, FeatureType featureType, String coapsBaseUrl, Integer fixedPort) { + this.certPrivateKey = certPrivateKey; + this.coapsBaseUrl = coapsBaseUrl; + this.config = createConfiguration(); + this.dtlsConnector = createDTLSConnector(fixedPort); + this.clientX509 = createClient(getFeatureTokenUrl(featureType)); + } + public void disconnect() { + if (clientX509 != null) { + clientX509.shutdown(); + } + } + + public CoapResponse postMethod(String requestBody) throws ConnectorException, IOException { + return this.postMethod(requestBody.getBytes()); + } + + public CoapResponse postMethod(byte[] requestBodyBytes) throws ConnectorException, IOException { + return clientX509.setTimeout(CLIENT_REQUEST_TIMEOUT).post(requestBodyBytes, MediaTypeRegistry.APPLICATION_JSON); + } + + public void postMethod(CoapHandler handler, String payload, int format) { + clientX509.setTimeout(CLIENT_REQUEST_TIMEOUT).post(handler, payload, format); + } + + public void postMethod(CoapHandler handler, byte[] payload) { + clientX509.setTimeout(CLIENT_REQUEST_TIMEOUT).post(handler, payload, MediaTypeRegistry.APPLICATION_JSON); + } + public void postMethod(CoapHandler handler, byte[] payload, int format) { + clientX509.setTimeout(CLIENT_REQUEST_TIMEOUT).post(handler, payload, format); + } + + public CoapResponse getMethod() throws ConnectorException, IOException { + return clientX509.setTimeout(CLIENT_REQUEST_TIMEOUT).get(); + } + + public CoapObserveRelation getObserveRelation(CoapTestCallback callback) { + return getObserveRelation(callback, true); + } + + public CoapObserveRelation getObserveRelation(CoapTestCallback callback, boolean confirmable) { + Request request = Request.newGet().setObserve(); + request.setType(confirmable ? CoAP.Type.CON : CoAP.Type.NON); + return clientX509.observe(request, callback); + } + + public void setURI(String featureTokenUrl) { + if (clientX509 == null) { + throw new RuntimeException("Failed to connect! CoapClient is not initialized!"); + } + clientX509.setURI(featureTokenUrl); + } + + public void setURI(String accessToken, FeatureType featureType) { + if (featureType == null) { + featureType = FeatureType.ATTRIBUTES; + } + setURI(getFeatureTokenUrl(accessToken, featureType)); + } + + public void useCONs() { + if (clientX509 == null) { + throw new RuntimeException("Failed to connect! CoapClient is not initialized!"); + } + type = CoAP.Type.CON; + clientX509.useCONs(); + } + + public void useNONs() { + if (clientX509 == null) { + throw new RuntimeException("Failed to connect! CoapClient is not initialized!"); + } + type = CoAP.Type.NON; + clientX509.useNONs(); + } + + private Configuration createConfiguration() { + Configuration clientCoapConfig = new Configuration(); + clientCoapConfig.set(CoapConfig.BLOCKWISE_STRICT_BLOCK2_OPTION, true); + clientCoapConfig.set(CoapConfig.BLOCKWISE_ENTITY_TOO_LARGE_AUTO_FAILOVER, true); + clientCoapConfig.set(CoapConfig.BLOCKWISE_STATUS_LIFETIME, DEFAULT_BLOCKWISE_STATUS_LIFETIME_IN_SECONDS, TimeUnit.SECONDS); + clientCoapConfig.set(CoapConfig.MAX_RESOURCE_BODY_SIZE, 256 * 1024 * 1024); + clientCoapConfig.set(CoapConfig.RESPONSE_MATCHING, CoapConfig.MatcherMode.RELAXED); + clientCoapConfig.set(CoapConfig.PREFERRED_BLOCK_SIZE, 1024); + clientCoapConfig.set(CoapConfig.MAX_MESSAGE_SIZE, 1024); + clientCoapConfig.set(DTLS_ROLE, CLIENT_ONLY); + clientCoapConfig.set(DTLS_MAX_RETRANSMISSIONS, 2); + clientCoapConfig.set(DTLS_RETRANSMISSION_TIMEOUT, 5000, MILLISECONDS); + clientCoapConfig.set(DTLS_MAX_RETRANSMISSION_TIMEOUT, 60000, TimeUnit.MILLISECONDS); + clientCoapConfig.set(DTLS_USE_HELLO_VERIFY_REQUEST, false); + clientCoapConfig.set(DTLS_VERIFY_SERVER_CERTIFICATES_SUBJECT, false); + clientCoapConfig.set(DTLS_MAX_FRAGMENTED_HANDSHAKE_MESSAGE_LENGTH, 22490); + clientCoapConfig.set(DTLS_AUTO_HANDSHAKE_TIMEOUT, 100000, TimeUnit.MILLISECONDS); + clientCoapConfig.set(DTLS_MAX_PENDING_HANDSHAKE_RESULT_JOBS, 64); + clientCoapConfig.set(DTLS_USE_MULTI_HANDSHAKE_MESSAGE_RECORDS, false); + clientCoapConfig.set(DTLS_RECEIVE_BUFFER_SIZE, 8192); + clientCoapConfig.setTransient(DTLS_RECOMMENDED_CIPHER_SUITES_ONLY); + SignatureAndHashAlgorithmsDefinition algorithmsDefinition = new SignatureAndHashAlgorithmsDefinition(MODULE + "SIGNATURE_AND_HASH_ALGORITHMS", "List of DTLS signature- and hash-algorithms.\nValues e.g SHA256withECDSA or ED25519."); + SignatureAndHashAlgorithm SHA384_WITH_RSA = new SignatureAndHashAlgorithm(HashAlgorithm.SHA384, + SignatureAlgorithm.RSA); + List algorithms = null; + try { + algorithms = algorithmsDefinition.checkValue(Arrays.asList(SHA256_WITH_ECDSA, SHA256_WITH_RSA, SHA384_WITH_ECDSA, SHA384_WITH_RSA)); + } catch (ValueException e) { + throw new RuntimeException(e); + } + clientCoapConfig.setTransient(DTLS_SIGNATURE_AND_HASH_ALGORITHMS); + clientCoapConfig.set(DTLS_SIGNATURE_AND_HASH_ALGORITHMS, algorithms); + clientCoapConfig.setTransient(DTLS_CIPHER_SUITES); + clientCoapConfig.set(DTLS_CIPHER_SUITES, Arrays.asList(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)); + clientCoapConfig.setTransient(DTLS_VERIFY_SERVER_CERTIFICATES_SUBJECT); + clientCoapConfig.set(DTLS_VERIFY_SERVER_CERTIFICATES_SUBJECT, false); + return clientCoapConfig; + } + + private DTLSConnector createDTLSConnector(Integer fixedPort) { + try { + // Create DTLS config client + DtlsConnectorConfig.Builder configBuilder = new DtlsConnectorConfig.Builder(this.config); + configBuilder.setAdvancedCertificateVerifier(new TbAdvancedCertificateVerifier()); + X509Certificate[] certificateChainClient = new X509Certificate[]{this.certPrivateKey.getCert()}; + CertificateProvider certificateProvider = new SingleCertificateProvider(this.certPrivateKey.getPrivateKey(), certificateChainClient, Collections.singletonList(CertificateType.X_509)); + configBuilder.setCertificateIdentityProvider(certificateProvider); + if (fixedPort != null) { + InetSocketAddress localAddress = new InetSocketAddress("0.0.0.0", fixedPort); + configBuilder.setAddress(localAddress); + configBuilder.setReuseAddress(true); + } + return new DTLSConnector(configBuilder.build()); + } catch (Exception e) { + throw new RuntimeException("", e); + } + } + + private CoapClient createClient(String featureTokenUrl) { + CoapClient client = new CoapClient(featureTokenUrl); + CoapEndpoint.Builder builder = new CoapEndpoint.Builder(); + builder.setConnector(dtlsConnector); + client.setEndpoint(builder.build()); + return client; + } + + public String getFeatureTokenUrl(FeatureType featureType) { + return this.coapsBaseUrl + featureType.name().toLowerCase(); + } + + public String getFeatureTokenUrl(String token, FeatureType featureType) { + return this.coapsBaseUrl + token + "/" + featureType.name().toLowerCase(); + } +} + diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/x509/TbAdvancedCertificateVerifier.java b/application/src/test/java/org/thingsboard/server/transport/coap/x509/TbAdvancedCertificateVerifier.java new file mode 100644 index 0000000000..a08746eefa --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/x509/TbAdvancedCertificateVerifier.java @@ -0,0 +1,129 @@ +/** + * 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. + */ +package org.thingsboard.server.transport.coap.x509; + +import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.scandium.dtls.AlertMessage; +import org.eclipse.californium.scandium.dtls.AlertMessage.AlertDescription; +import org.eclipse.californium.scandium.dtls.AlertMessage.AlertLevel; +import org.eclipse.californium.scandium.dtls.CertificateMessage; +import org.eclipse.californium.scandium.dtls.CertificateType; +import org.eclipse.californium.scandium.dtls.CertificateVerificationResult; +import org.eclipse.californium.scandium.dtls.ConnectionId; +import org.eclipse.californium.scandium.dtls.HandshakeException; +import org.eclipse.californium.scandium.dtls.HandshakeResultHandler; +import org.eclipse.californium.scandium.dtls.x509.NewAdvancedCertificateVerifier; +import org.eclipse.californium.scandium.util.ServerNames; + +import javax.security.auth.x500.X500Principal; +import java.net.InetSocketAddress; +import java.security.PublicKey; +import java.security.cert.CertPath; +import java.util.Arrays; +import java.util.List; + +@Slf4j +public class TbAdvancedCertificateVerifier implements NewAdvancedCertificateVerifier { + + private HandshakeResultHandler resultHandler; + /** + * Get the list of supported certificate types in order of preference. + * + * @return the list of supported certificate types. + * @since 3.0 (renamed from getSupportedCertificateType) + */ + @Override + public List getSupportedCertificateTypes() { + return Arrays.asList(CertificateType.X_509, CertificateType.RAW_PUBLIC_KEY); + } + + /** + * Validates the certificate provided by the the peer as part of the + * certificate message. + *

    + * If a x509 certificate chain is provided in the certificate message, + * validate the chain and key usage. If a RawPublicKey certificate is + * provided, check, if this public key is trusted. + * + * @param cid connection ID + * @param serverName indicated server names. May be {@code null}, if not + * available or SNI is not enabled. + * @param remotePeer socket address of remote peer + * @param clientUsage indicator to check certificate usage. {@code true}, + * check key usage for client, {@code false} for server. + * @param verifySubject {@code true} to verify the certificate's subjects, + * {@code false}, if not. + * @param truncateCertificatePath {@code true} truncate certificate path at + * a trusted certificate before validation. + * @param message certificate message to be validated + * @return certificate verification result, or {@code null}, if result is + * provided asynchronous. + * @since 3.0 (removed DTLSSession session, added remotePeer and + * verifySubject) + */ + @Override + public CertificateVerificationResult verifyCertificate(ConnectionId cid, ServerNames serverName, InetSocketAddress remotePeer, + boolean clientUsage, boolean verifySubject, boolean truncateCertificatePath, + CertificateMessage message) { + CertPath certChain = message.getCertificateChain(); + CertificateVerificationResult result; + + if (certChain == null) { + PublicKey publicKey = message.getPublicKey(); + result = new CertificateVerificationResult(cid, publicKey, null); + } else { + if (message.getCertificateChain().getCertificates().isEmpty()) { + result = new CertificateVerificationResult(cid, new HandshakeException("Empty certificate chain", + new AlertMessage(AlertLevel.FATAL, AlertDescription.BAD_CERTIFICATE)), null); + } else { + result = new CertificateVerificationResult(cid, certChain, null); + } + } + + return result; + } + + /** + * Return an list of certificate authorities which are trusted + * for authenticating peers. + * + * @return a non-null (possibly empty) list of accepted CA issuers. + */ + @Override + public List getAcceptedIssuers() { + log.trace("getAcceptedIssuers: return null"); + return null; + } + + /** + * Set the handler for asynchronous handshake results. + *

    + * Called during initialization of the {link DTLSConnector}. Synchronous + * implementations may just ignore this using an empty implementation. + * + * @param resultHandler handler for asynchronous master secret results. This + * handler MUST NOT be called from the thread calling + * {@link #verifyCertificate(ConnectionId, ServerNames, InetSocketAddress, boolean, boolean, boolean, CertificateMessage)}, + * instead just return the result there. + */ + @Override + public void setResultHandler(HandshakeResultHandler resultHandler) { + if (this.resultHandler != null && resultHandler != null && this.resultHandler != resultHandler) { + throw new IllegalStateException("handshake result handler already set!"); + } + this.resultHandler = resultHandler; + } +} diff --git a/application/src/test/resources/coap/credentials/client/cert.pem b/application/src/test/resources/coap/credentials/client/cert.pem new file mode 100644 index 0000000000..4d385a588f --- /dev/null +++ b/application/src/test/resources/coap/credentials/client/cert.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB/TCCAaOgAwIBAgIIVNrVgKT9OE8wCgYIKoZIzj0EAwIwWjEOMAwGA1UEAxMF +Y2YtY2ExFDASBgNVBAsTC0NhbGlmb3JuaXVtMRQwEgYDVQQKEwtFY2xpcHNlIElv +VDEPMA0GA1UEBxMGT3R0YXdhMQswCQYDVQQGEwJDQTAeFw0yMzEwMjYwODA4MjJa +Fw0yNTEwMjUwODA4MjJaMF4xEjAQBgNVBAMTCWNmLWNsaWVudDEUMBIGA1UECxML +Q2FsaWZvcm5pdW0xFDASBgNVBAoTC0VjbGlwc2UgSW9UMQ8wDQYDVQQHEwZPdHRh +d2ExCzAJBgNVBAYTAkNBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEQxYO5/M5 +ie6+3QPOaAy5MD6CkFILZwIb2rOBCX/EWPaocX1H+eynUnaEEbmqxeN6rnI/pH19 +j4PtsegfHLrzzaNPME0wHQYDVR0OBBYEFKwEDLTJ+5cQoZfbjWN1vJ2ssgK+MAsG +A1UdDwQEAwIHgDAfBgNVHSMEGDAWgBSxVzoI1TL87++hsUb9vQwqODzgUTAKBggq +hkjOPQQDAgNIADBFAiA2KCOw3n2AK9Vm8u2u1bQREIEs3tKAU7eFjpNFn929NwIh +AInhBGoEwS2Xlu5bdZSfWnujoRrEQiIiQpStmLxVcIsH +-----END CERTIFICATE----- diff --git a/application/src/test/resources/coap/credentials/client/cert_01.pem b/application/src/test/resources/coap/credentials/client/cert_01.pem new file mode 100644 index 0000000000..3b97ab4aad --- /dev/null +++ b/application/src/test/resources/coap/credentials/client/cert_01.pem @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICIzCCAcmgAwIBAgIUZZCGYm65c9vU0Xfvd/pAnLVDouUwCgYIKoZIzj0EAwIw +ZzELMAkGA1UEBhMCVUExDTALBgNVBAgMBEtpeXYxDTALBgNVBAcMBEtpeXYxFDAS +BgNVBAoMC1RoaW5nc2JvYXJkMRIwEAYDVQQLDAlkZXZlbG9wZXIxEDAOBgNVBAMM +B2NlcnRfMDEwHhcNMjQxMjE4MTU1NjE1WhcNMjUxMjE4MTU1NjE1WjBnMQswCQYD +VQQGEwJVQTENMAsGA1UECAwES2l5djENMAsGA1UEBwwES2l5djEUMBIGA1UECgwL +VGhpbmdzYm9hcmQxEjAQBgNVBAsMCWRldmVsb3BlcjEQMA4GA1UEAwwHY2VydF8w +MTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABNU1tE6o/QpqJJqpy+m+UoPuQe5g +eTgS4M3x0iQS6pzNEJBhzbnOp/BysGMB4wKiAWTRuKdH/gcRXDBTjLd/d7ijUzBR +MB0GA1UdDgQWBBSiao1iNWYzlsrSbxYqbda116HG1jAfBgNVHSMEGDAWgBSiao1i +NWYzlsrSbxYqbda116HG1jAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0gA +MEUCIB2aCM/nvDqic9NkoSX/71GwksLiAKiFNkt2BZQykrcHAiEAr2h5IMdkyurN +Jy/idx2y44CP0tMq/3QV0QLCQFJIi6s= +-----END CERTIFICATE----- diff --git a/application/src/test/resources/coap/credentials/client/key.pem b/application/src/test/resources/coap/credentials/client/key.pem new file mode 100644 index 0000000000..02ca740c93 --- /dev/null +++ b/application/src/test/resources/coap/credentials/client/key.pem @@ -0,0 +1,4 @@ +-----BEGIN PRIVATE KEY----- +MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCDn0+4CuLeX7xwBs0ts +UUEDB3+HRwRKdIPeJlIbKuvvEQ== +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/application/src/test/resources/coap/credentials/client/key_01.pem b/application/src/test/resources/coap/credentials/client/key_01.pem new file mode 100644 index 0000000000..d5918e8181 --- /dev/null +++ b/application/src/test/resources/coap/credentials/client/key_01.pem @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqhkjOPQMBBw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIJldU1MBuJUJnNHa9Ob5NGlXc/Os6put9eh1TlIbuScnoAoGCCqGSM49 +AwEHoUQDQgAE1TW0Tqj9CmokmqnL6b5Sg+5B7mB5OBLgzfHSJBLqnM0QkGHNuc6n +8HKwYwHjAqIBZNG4p0f+BxFcMFOMt393uA== +-----END EC PRIVATE KEY----- diff --git a/application/src/test/resources/coap/credentials/coapclientTest.jks b/application/src/test/resources/coap/credentials/coapclientTest.jks new file mode 100644 index 0000000000000000000000000000000000000000..ca8c8ed1d77f31bab0711a31eeb63ae952c933ef GIT binary patch literal 20462 zcmbr@Q;;alwkYbhZQHhO+qP}nwr$(CZFkRh_iWpJ{&Vk*y< z^R6g<+ruL7Dv=RT~Yo{Q>KFbfYCr zLPYiG?f?QJM*spOV1NOG_Y8-7oWb`&^5VkYFEd( zz8^saYzfmK+kCwq)I1+Ax|H4}ujyDbEcLsvKozwqmbDx&gkG_+V(kpPDAW%#jPB4_ zFclg+cY+|X7#apGG_1#Wdn1&Fa zYIc=_6FA|hpDiEhu8UKYiD=Qo8 ze<}XXE7f55=dmFfF>@q27VVlrRsFm8O%Y za@qeKag*^6teqx7o-IyjeGND2fq%qhiV2aG9CmI)xi+U;jyqYyvI`9-XACk)1-|!Y z?UC?|g_hIdG{_a@ba1PI*X0BparAAV&hA25hjLrA&{C9;Spr?gG^;m3jUFIK;;Ri3 zA~}ve|CWMkX1yPtParH;>dG6qOM`&h+0R?`k1;KbZbW!N=+8{_4>R;hKa=r zV(=9SZZ$XpumNTKZ^T*tg*ZDC(|@|FC~PE^QXKfOsMkl2lp>xdzjD6^{5#^1E$*`k z-&M*&xGsj&@!k>th=<#uZCVkA;=WR4U9;j5=8zXh`1!{GWI#rs1%CV z8=7A)*rU!dq+u6%(KVLbx=>gCG!b$X_(0>6i@UW=mbEdss8xK1tFBC9)J`SphBbUU z<5*v*M{r1YgvCmx`xCHFrgSvLp)t3%R|woa&**42>{;T3upj~DVb-8!LiLw_gE>ls zx8eVvxC?+2fa^aw^#5j5I{^ECCC<+I&#odi_+RN29IPNntm&Un9S|VEKTY|6j{yFw z>V<~lbP7GtdzxM7CGYv*0!8qTmCwKP4VqAD%u9RpTq|{_7S%K;V*`T37?LnZG!0r+ zIGX*AjgFSKz@|nkeRHrwlTS%Y^b5m&01k*6u-wKH=4dhh0R3qyI-F<7k@m`!tLYT{ zE1L$g(c#S)TS?vKZgf_HFigSgsft%_Qz0u8FLU@K>}nErdGj#2aek_nPqkcUC6Ybb z?Q*lyvv8qV*H?v=FP`iz7{$tx#6hq#oAKdooD7PHZNrA9GT_XrKuma8;r#p!FHYzz z-|ACL5pdJ{YV??yHIhZ@gBIHe{8v@2s9l~=pL6-)3BBZN0Mx$0qz6aj=^7j#eHFASvO_h$X}9X#8pc4wVLCC`7tl)6i)UC%g* z!_AzPPz5q0H($UP@e3gZxg)xjWnjIhRi);L%eH_5H9oYGX@pD(l^v~xv zC|a{?*n5bTzK#h~>$E;V(xrr~dKusHxv@uc78j_6+WnB{3#I8c4$HoV#cyI8KDBl)4}y7(7{`^F(<6>UlD6Y*)Mec zCEwQ|(=CaPzm_m2jRj)_*$a-H-@*o|f0ZgCTLHccYfBdXY+mJAj>p!q%~O}4R(7Z% zb!Z=4>RR`Z&+oUB%H#a)`fb*|sAGBC+%z>twrO8p$^Ytlq{50PTrIS^<+X5^Y zy6Zvm{|m5~1I*}Dq;DAfQd-D(b>xjBsNg*6L%=1g$8C@^nU8%y`Q1t%F&n_hAie~u z0WSrPNIP%oJXJC$0-Zlx0o=F_D?a3D6GLq4j;0LoJt#A#f_9EDi^J*9jyr$m{zF-x z`KC6n!1t3;|DlG%G^S_J$oFMRh8_KB%d{YwxfvhgoCq-DsD{KrYrbHET2&AwN;nuD z>(V;I&}oa7{{uxWEVLeTW5I_mxXM1jK;}LuKM$9U!r{{tP*(NT%{pFxU^B)Oed+9dujiQ7D^4hcU4; ztI7$Z-*5=wcF+rBiNFW%&5a-w9XePwhrgQ@$-?tZhi-S;+TbwD;Lb+f7at$hc-jug zO-%V6q?f-G2s4oHh+28|$meJCgea@P{%Ct-x*&Aq(*-THfd`ev#Kig5Z}rLdbTcx0 zpO9f$^)O%fc5Wwu_F&}a`gS_!q!0=DH1#rD9sKvQgHfsG0g-8MY(QTqS{ajl9?l7U zaOn->xAda69`%x4D3wbW`D;pZlD(hv0H8einK;Q+dDn`6curtP1Cs1*Mx&I0pdOEy?b@vMjjo)^V zs@P5}Z2pbX*5hC#9gXqD#0Jqz6QGclyZL2=>|7yv|A8_Br=H=j^qO+5QVKs90`*~H zi(r#q3|L!?dt5(GAjR-G_cw*2D{`Bm5x-9pP0UfgGJQgkLF5U9aC*$c`qPG29~3@;?|yQ5*RN2#;z_4z+9cRY z=?P7wA~Jh?p3Z1~SdR#*cIagK2t<_*OA#J>7ew~a{8G@urMh*%OYYuKn;$OUGBfU& zj(o-dhFQ8Rl=W)3)5Em|zZ9A-7)C?R4E7H!JE3=f7Efl_? zG>rxZ>-MZUB$h5R00&&30z=#UuYFd~OAvNF!S^%#_ID##mh3&KsL-(X?=f}--Twv{Xf?XwAaY*8e$J?z05kiZ*(PbviF^^Sp@;$@ok|` z)oen%U`Wr@^*VgwqDUA!Jy@zqK|P-W;1a=_#amFvFZMMkX*o+dq=;MgIln@RjN=z_ zyCptFuSb9A4}5+(v1hlC3Z0C0)Hy1fR`16bNa}aw7_K+Rm4_pbSQ{4OO{?gB($5rP zuz(~8>8EZ6Q=YPOL?PiBngca3Y5Ezw8{nHl_PIqSJ{4f!tJ9KR!9^MLX}Vb6pwp5F zA!A$sG$LN~`a}f)Yz~ag#|~hAaEOD^a54%&Q0O(H^p*7)vR-f8;ban=I&H~(!a#6T z=hxF&n_?(f8lK$i?=tu702;S~+<6Nc9pmH&_JjB{mqGoYRAhYk=O!BJ^Rs&Snc7(4 z#<_Sm3}j{Nu7O}7ym~X5fq>tJo5)R0Od6bIkC5|7YQ~cVJze3)>d$6xvEF_aRL7kM z38L3#tmr)HKsRL=VNssT*r$gs*iOgl71+OlP+s1n`%59ql3*S)<&mIbJNvZ9P$+yK zsIsksZvS|;vWbhQ!kc;lS%u;wsM}!>5YLh+_cuV)T8Q=lqHJ#@DTXWWk(Xnz^1r!d zdZ3WvS{21Pu|}cm_3#kaG+6`qD-N}cqvoJx7FZ3?LG|+0^fsPSW7#q=QeW+)w9b20 z_K9uLw-5`TUg*tjib=_=TMr$tmo@#B-vE1>zOGX%ncmTfh;-P?Y%#U4UAcfBZ9JH`VbN^8@-m5o;7l!GkBJ~s(0#V+S-2uw9 zh8{kmkb%in6OI!PN67dgmfzxR^YDOdOZP$%7h;@(jyC!7Lc z@Oag=%GfK_f@DD!8M$!-_H(mdX^*i3IJ?7+akbBs8#*^_1zdJ*ZEX!H>M|JkZH#T! zBkH5cGLJFo*w$^mg_LoV#!9$`U|B6e@(Kq7Q_#)FiGvD4l2q(5SclpcgVcdlz@01(q9va*ZqwIMbi5~o4{Zsl z>_{gOjW)01YYheN`mp~Edui*!au`L9&A`B}Z54nCkF32l1KczB-yB$+NN)|f#c)}< z`dgTatZ?5oAgbx;r0hl{h}gcWnu$&O(_m!F^G~H?rQzC@soX}2i22MzsAWx7mIY00 znJlA~WEkzWu@4aao~Dy2DTJ1o4H3)4-eoWYy^9#@jBJ^1Pd;1Hlg0n--VzW8?NU_z zhXwdGT~#KU3_ii?+oI7qC^*g)EPrVdt5a^vh2ii}tp2zor0WDM)7nD}?4-2hS!S{- z^!c+fF7Os98hSRO$YhAqgb|_a!%HU2%1W9kJKdtC@?uLcOX1WrM)dx1W@_q z_3}M&nCSWWC4aZ}OF}%{MT7Wv5mNx43`Gzq&fEsj4>_}h=sQ`;T+@b>$VXe=wSo;U zIt0~0tGK9f`4BntK)Ltg&j1J3DiuWZtb*$5tE!__`nmd6yfmEZW)0h6Sc*ovY(&W? zvfow%1k}TyQSn2#=Tc1cel%GCZonjNtFs)&vXQfxp_F;)!Irt(2YMPK^5wLrNeNaS zCXo!+-16UdC5nys!`5DcKD@WOH>2j=CVHcLiE#{Cqe1#@fYE-z7(a%9!iaQ}OazE7 z6Jc_^bi0oPmON9Zp6^3w=tAw1Y2^>PHY~NTXs1ZRzT-9`znqUyx*(lz%Ts`fQu=B%TD7<$OtO!LmC2kt!&a(z#~EZoW*~%=Bb89%WSz=#1iaB+|HpH8}O zau&$cF+7Z3O4PhUt|;S6r=jMut$UBF0~wP1X{v4#my`vGWQ7?j5Z%sP%)L&L29)7F zwq#%>v~j*@NDDGV8UpatRW*h`SL?4lGX(V6OR-%u%O=g<0#C|#s9q5V2%Bfd@iTaf zWL((PBoTK&77=*8Bf#+E^L;q(cK$@DH_jw+JBBubWq~(VgmvSuRSNg|=@v#sMCE;&vkk=V02@hoFk>J4fe2fOG@p z2IuHOxRY6Cprs2(G0NI!xjXGLsz!{u$NM6tqv#fENN2qJci}6^kwx)fVS9SyFQLUI zOkQurr`vk3Xq;afEh!c!H?-c$>gCo%lWF+8(8k(P>68^PnEFynfO>hW!;pP|cgh)W z3yeA6MWwUWg0Ac*4PnnLo$0mkgg@2hSU}9KVAnOSK-ujW{V7zurd-89+K}SnN>wfa zYGcyODhP$XVyifE%YDwNHhnO(8)C_*f|-<(3f?|>5z^xv4|iyjMfvjP4HgFtRH6$o zlS$^H_nEN|B4Cd{K^g0sbp>$NEFAV{*-%;cNJIBvIg`~GEc%OKJDD5~1*}PW-Y^>( z4{L>!pFI1q(tmzN3pH-(i|>skalRWj@aR<9_s7S#yeP+i5XAZxoY0epsK71kj%{s` z1B?#h5M}ebO2{xMjU3_Vi{x-=cA2Txkd)1P`o})=d?l1BiRtgBhJaoX4>4YrH~L8+ z#sj_dn(9o&_yjU^h%>nF8UTqKyg4uaFvrbF!5MYZ#0njPUptiZlS!QpQL$E4PED@7 zg6FJmmyh9OMkc?13^VlC)KwnlfZ}OrzL{htP?3!$=dd|EiOjWv)G9}-zH(eECTpc1 zDRMvjz2zDxA=rG_^5gL zXM?mL;P?t(Q0Qp7&+3Cp!H{d8q&6Z4r=!8rP0{_^_#_i8S0vxWD{k*w3;Juw!2&&c zB$)E!4AlM#Z9D^~wnaw3?X0nnP}@^JVU%=-=Ig1;3y+)&`c^>C1Cl~)PqMt1M){}4 z z8Ycse&LxPB6tj(~84FM=Ymk`uyhaOc1nPhjqtTVUuEPve(TnAH*8{gmfzk(e9|^nC zO&8dv0X@^?xRbtoX#HlQM3P%yl%fT-|Xshij+iQ&3{+cYb}op*0%%%FFVG`WXAnuzfs3vuZVyiwg+qhQ&#YfQmnOev3YR&Fs|`_w zvd6=6<22})n8JA-Pc`K5Xu|ITWkSh0{loqpH9TxHe5Q8#ZkA@M7}rRDd97vcr{(aK(_NO*AuoQXip%VO3*IND{R*i`{< z)R%+f)}YwwP^@Jsa4xv|@u4q5q%|6rsl5{(;mplDe5J}dYhn8@VG>ix_j5M!h~27f zp~PsH79uu#tr|%pj?%+;SmeA&?Y8eB7>_Z$TMI+WpRz@Sk*W}x4H_${CqBv#a zf>W*e-W}7yPptkfa~ov-1>zP|_UoGnmkUKZ6ujCTAd-1Z3pzcPP$`|&2h8Ag@9<+&Z*!iX)(*Q`Mjx{K\hylOan*;kqm$M}?1Cu_<6QnGg~ z@4c)8L2ihDiI_Dg$~_}ZmMSM00R;O!q{(3L%5{QgccZ$WDT;eQUC6u4{&3s}TUiEF zDrF2{xe`cVzW-F7uYXZn+l~UqERv{-?DEDn_AOJW4GR;dMS!L}$fTmxIWln{vk)y+ z7r3UsHv`E%lcDeG``N$e<0`V|w221HENbMn$@*t-x}hA-S^BSkB>p%MCZ%1`h4V>y zblRom4^)+FB)rd4I{2_W-w;z*8VM(Mi;U+eo`lwhf_`Q7A6R^O#x}fk8GQB(sH&A~ zEiKk$tEi`oO?J_Ql}l@bm@y@bmtPIMXoJd7QIb@dyE~*FQpu4&^+c;ATk~q=PIUgf z$od`Pc+{$dx1w5M3<~j0LuP;NZaqx7vPLbh!xl_mI_eam#moFkAu<)8z817p`OP8O zm0QGjeYZFPFc%_2Xz=}4Ct=!*$6wlJqp5;v`;oJepMYT4eWgx%S%>$Z(H*uA?R(^) z|2!Uc`-UoI&wnIeR9UrXBDT1(wi26onmeL_CWaT-o0}qd0-_MxN$ggSZc9T8`T(Mk zqCojr40so%!-p#sU`EO0U$$4Bj0Ci>Ju4wTlogY`ua?b1^DzQ|ujbn6$F2wQugwQ> z7I>#(yEWU<$BJZ)<17J;TknXDXPI)vv=l zbg+&Y{Hf?LwXa^^!R>Vo89r^bhvh|7^jTh#LYvCftw32^Lu&6iY}w3}#K@Z_Ah*Cu zQ9Ihg_~Z1xovZA$=IPp75ufYDyK4>9>E_K_@s}FwcL@!;EvPkhrGE`m8IY0d+fl%y zbLo?H&Y)S_Q2~hQRpq19JHu(NICAxh)^JUVBk>_*Uf=gf0A}ULJ&r1{bc8M(Y+Q ziG+RGIWZKV?moiL4(7|Fd^Hc{O@0nzy`)iQrHJVaIy^ z(h{Jd6>{%A>9KGAxH4D#*T}I$S2lT|+BAtH3&+ZYaPvKLxX&>~{T}FKQ%h;+yfbyaXK8QX^pJ91bu|6drLu%c$u zOxxhk<-wRgzn>1!#}xzy8z`QKnS@kLLgTk!FZyB(0dOn0;6*12*Lqe)E8Os9jPUN` zN~m1)@>U+j8nGmzSiz4CTJr*Vu2J9pxR7 z6Y)vlAH(~I5@1gq)armBGXBpjQg|qX?eTah=7lt7%IRu(<3tYv+^<09I#o_29s-Be z7SEx`*7B(JVX&h!g2_bsIA+rx#IQUw8suw?TD*O~hZ3evi?h;HXs#oy+&>7t1~y%_&;2@!R5S*NyJ7Wl_-`qXeo6rW zQ1?7+v$DdQY4Y|E{4^v6ro9-@M%{e|+>u&k6Ps`fbd-dTLw;KNbr+-DhH&YZz1kKUB(;J(# z1Y-&8uoP+tC-_^I$=l1T6m`G^&{Cq0VV1ZVtyoS5F0xQi75Pedf2H5nFnZzS$Ga7> zf=v^h^i_E3RenJ3U4-_w_9$6m&I)CfUiw8Kf53^J3`WqmZS=0~V3dx$`@}P84z?hc z;>=+wRk53=JtC9pvaMG@T@?)8YC)h09&BS%(}2)qpm@H0#NSR)iX9lV31|(iVqf8d z&|7y+GI34;l?XD%xxUHV(`cjlFrji$xvz-MV7_#onZgQ{gZ!=!AT&*6+0InW_Xq^I zLF#SbdO4{6jyq&+`65s%)o^6yJ-y&^aV1QWaT@if&zxgKKWqNRoguqrEGKqFSHr2g z9&+xva6Hia*BBO$fHtqUWO)%-x-}_1&Zx?Iby@rz^=h9moT7+*UKSE;xz3PG_atBf z&BzGIbkxf&N)58rI7^I{p1Fa1x)QO58KbR4r~;#$o2mOFH8#tz)))-Bv$Km7wQR`l zG5IT2&`8JBNj-}~ZOonS_QgbM;5%i;JeJpyC}q~rVx0-gnq&wIH;8Z+8Om}|DlPGE z7a$$#v+wA((KHqT6lq6yu)Pu^92i+V)mH zZ8n(1jcw3te^J3EtVn{+LrTlqzxU6}12bf)a7KAL^4ZMM@$k%hmuhlG2?p7P9x6yE zV9ylB5GGK2SdA`_cSwAg^wzI(nz@X#6`PJLBUDwg*Xeb2!0|CWV~IjRqY?MTOS)g` z&`-@DA)_?|Uk{Qmp$cHbK=bc9h%%Wqd~g?n0J(4dCdO85Fr>x4${Di^{2 zNn|GQIm!4&H-%AAI134^ee`Bf24z>jkM(kc3t){iu_q^8)d*`1;`PPlcD~xjznFrk zcN%s!77sjUJvGc2Ie!`EOB1X&xqne&j~oK&fe>3o-ajnWv|1zDbd6Iwm0{Gt1RebH zNeSdRxY*GCk*Lv98ES2mJHkb%&wuOV8Z;lth|TatK#Cth6}~(RTibZ5J$#;cVl;ky za4>xU?qrEfmTpl)c(I&xLk#PMd)uC5)+u&maNG@gnO?dYgx+STTHL8aS-Ll*5wW5) z#)9*J_=1?z*PLOCNE2wSwlBY$O|_O^lJcctZ?ysDHGtOs#l)694LSEHAUO3Eu0?n}HHOiy6jhL~aa~$YH16C$iB@OyYGs z&*1K`>)NvGYL+M^@kS1>qW=1+L&a<|k>^mw4QampfZSs?=4&@T(nMjFtk(oO_YtX2 za25VLcxr6r9_Y*(oV=D!7n z%y(Mxi&&Z)*+r_C`s5W_w#uR?n=hU%*vx`5TJki}#r_n6 ze8Ae}i*Hs(Q1D{$ciIW-m?sphWYu+Dg(!VZ?VVRg7Ol#xDK4|>9%SmYhlk%+CbDS# zZj1v?;i&+R`=@fz?>wfvjiz!PXr$t2jCb`?Pi}jkVam5kQfoaNV+Ea@L3Y;`nHq4s zm#1g>7zKcQ57pi&-AFUU3_}@m!|W$7Cod3ai#KMgyg3K+-Akxd_U%J^@M7SQP^oA9 zKJ=3v*Kn2Xv{^IqdMGu*6g=~kI;I{L1zHcw1Op%8`q_x(EB2&0gL;@V1*oJJO)u*MPJXELw$>e&U=!0rO>?MrK?rM>&p*-r?pg25)KgUoi}?0+DDiu z$%r_+`s!WT+*-UiJ`?hBMtQ-&uPJR}h*fG&~u#PG(PnbrQOn&P7GT zKzn;Uy-;$}+e3R`n>=tDz%U34O{+$nV`xDG&_}UdUUmw!8#8v6#STLfXT+tsJ}FNw zBq!32j-Jx?)>7d&O$o-cpgo8leHcMynggwZ20*t*O zo@dP7-J(z`Q)mUYo5%sQ1g_#LR9?=dO0me4+Ns+y3M?9uptc7B zP*Rj1D4=&Gbq2PkgM>NI1G%seJ`=XKQfUHCfMS^e`Bi1Y(090dL6DuOKWYo(mb#LF6vUrB z<)GAroE@S-3A6i-JTMCrqQ46*YF>BgfrY=D4|TV~?h`(bfo&h-caR4dw|%3KW@?}Z zc6#bB^6Sr68!+5R+s;l8%zdjEEIj7-!C5_^Uzmk8o9rI4Tn=Aw=q&^_l*bLt zGe7{})J&?Y)g^I>0N*gi`}uQjd15xx?MtqSEktxC1BX~OUZ;wVI1Lh;QcF!YNi|Rb zuR4$PyvZ=tt`jca@u_gmLL4V@WZabYnyP8Cjz1qIVKoZpEd!!MOu(U7KR^gW@v`o~ zX)k!CZAv*o{`Cv9ibcIpu}eax2rBGjO6$TH@=gNg@|1jlvDE_qPMwvjALX%<2{k-I z;S!31x^C;Pftn5`O$)UoM>_pH7X-x)7Zv2uxJ{+z3@a~DY0F(^YRWV5He^2X;`a@@ z_r9z0jYB#vdXM}(u9M;yJoiRfO~m{uZ2JT2<9ta35Mid^Q~8xTfDAkT?=-(46as{! zHH#5R`1gpIUYn)`PW3!d)jW>Z zkel1#j5Y7jy4;DNAnHOfHq4P9hI&{C_{Aj@eE?k^p5x!%X_O>i{SRJ`xJ|sh@NNf` zHC-TK*r1BkhKzQ;)wRRjO-qoKzJ*2hI4zimNETO255P6Ikxd9mzSR$`KL5RTFr!?` zA_D1BECr?*WE5?Z5%!X6Y~PAJT2w*7*&Upki~8w8@IThLvZbz1l!Q%$vqD+MP5t)i zw8HHcVVd!5Jk(Og>CT%JhUj2e=_jgqQFH)$V7w<;FAqrKf?uW8JbgQCh?gE_`fQ$f zh#%fix>(+*HPv4;GmO5J3f#-7qkws=Db_5>T zQxTEvhZA|LSyvdEAx}4a3X9Z?AS+?f3Xh%~+J$6@(CDT*UtN3t7JpHa`l3$5aBP9= zZgN)xJlhetpXh>6_DMTjayj5)2NWWYg6kE2%60cHuZ8V|uOOE|G*sW~jQ_qM%YiFG z0%?BTDNwBWz_?koE6IWq*Hg3aYqG*x2US}?l37*72!uy)FxdRc%ck)zYd?2LO|l%P zZIC>0pC|})s&RYGIvNH{qR9?{*A$wJdJJ0mM9I;Wo(G=t+yy~d_MkkiXtkF+xKT7D zO{*o$NEO=`H;rNtnw**pqF_EmUNHmi8=26x;fX7jHz8C425xG2BY+>>%Bh@y-q-D8>w-dVZS7*zOxibYS` zEl!UShaxF1xB3lF%)C@m)8y9?OP}dpu%>2CuoNpcjhGq6LqPXWnQu|2kJdGAazia- zKU|C0r>+aGDW@E=&CulGhb@LAb&awoBU`W3JvwC(0sgM$@BT9K^^5LfG@mr zEMVV%cFIi*4*p83hhD0|n#5F)%sXGTUV`PTL}h>M?)4ea>aOju+BuCm961wlkgpom zjE2FEnbmC|!pk4Jij8=~LoifSh_D8Yh<3C)2jLp-^CK;Ly6(!K9*|9{I`Ub#^FdKI zI_GTZ>G5C+$T#xkwC!Yyy6{PN*ImBp;K<8&QG9qOT0F_**Oz#`3nYy#TS1@wrS9f# zuqUtxzz*s#=SV*hGK~8XD6!xkvRc2Yfxb0mzjX6XSQsCEkVz1}?;D@8&Cza!b7nCj>pK#Hm zax>v?#bC+fzGs*Z9VghC_!v%8g>gSxz9W0E^24z^ZZF#ml|xpvJ1Fv%+a~1|sw{iL zND-yd9iCPDZrFEM+ms^q!%EdV8(8fkv*HCOBrh)>?@2F-CpER5dVp6wc)g#bE$L3; zy8F?D;krXs!ZVd0FkPUXj)MdXkC>;neT+)>cYh{g1IQZ~?Ub~~FSQp=2k|*%#PT8Y z6EZiEiC)vfj{^Rkg^M^`toFx)pW+RSq##cCd#rZvu=u^~T%YOG|E!eC==lpzH3l~m z<->zQ5KW4YX%+q_?tJ(On*ZF(0o7=I6#1iP0P-unotfZoP-4ve7mZ8Fg>o+S$me6< z^5x$9R$?)Ae&4l6HzbdAP}Fn+uQSFJ+N|q&2N#p=&)TP6$qMCdPQ&pldINmuIS`6895e3&;a(Ph$DE~gXjsO>HZqm8Lbic+&@3&9^~BmQ!AjYu$d*EvdzmJ zyGBS!Qp2-;s3OHCD1@>m24x4z$1KFGoEq1?u2>?_{Ae|Cbs>BRiT*i!#u$LAukj8? z6?+N!A39Cbe~7u=5;X~_Lzf_H$h?HStJ>2mU=k|`-1PEVz6Tm{V|$(Qu0Q%4vRdPE zHYSNP>LdARS_w&&woR)*ajvd19-tH(Js2D*QtI$@)=QZJ5b>DYQ<2Z`*ES>)#ZX}j z*il6em$c;AUTYj1;gTv_MX~`VuoSJb*Q<*vcOsr`Qrqah*2^r87u2LG7g=Fe67Fq~ z?jqik>A3UF#qCr$!4B2AiX#V~fiIsDm~x1?*&BI5vp7$fi@8{X@?M+udp zL@J^sH-7fmUIgZju)H0QZI>g<#fmg!t5h#0I9+kb8Cy~q-#!sQIGS7JO2O2xsfL{LNlei7H4K?YL~Qs{KHUV0h)&-0xGG9qv~l^z~!?9%A5!IM7D@6_^e z!9@XFOF@^Xl27X7z@ye^(C&s1`bs;X&VtIOgHRt7TB@g6imLG z)P~d#bG`1}Ztd^X{?!*#+~=hYL$NiNGf|LhacpZHS^F<&EF!+gVB zZaU*wf!iJg5KChVD$nTxs~q0k4<{{gm70D)K$jxd$kz$-yTju0;O{hyjEL z!!MIi1!cAi1ZLQ7KGbJWPlqv@PO_XFgeMVq+9L%QWaidpe0`5GrodQM9hcr9 z7(n!ePr9b($(rc0>kZm(b}mP*-1uA}XkYOiyu`5m>6ka?zPayG4tGuA$1qOx4r^X$_Ljvr zL-}pbp*mezfC%a^D_;*+-l-WG!-OiaT{v&gWpv{fGTW6RO8P%$l^DJzwONmOAYmGO zJtAz*YplpLkZlGESU7``jmdMbA$Fl#q}=!bRo{n8GR!@TkK3L2{j*S&o_3{Cz(Py{dVzjZ81GxIko zc_6J;MzwV@($MTIWT(mFTDL+zH`9s%WQSLH;Swlm==x^vQ8P@~Kg{cd-2_wU-SWl* z<(ve8w#&>7;fuIY6$>-7fiEdVWl?q)VHq^rhXqK4Ul|}SSy4Igpd`OCg+O56=bTdn zsBkfBhi^-F8QYeh*A}WU(1u(@v8Bf)>i|CjLm9?#z>|kYIOH2bqu_D%=LzAK5@M#x za{X@}RUbHvlKpC{5A)&c#`CTa{XL6#tUHY}xL+t_C+@shf@U4?K^;V_LDjuq<%5(H z3@x47<;;o*h3){%*RGtkK5GG@lfgwof?yW zYx^j5(-PKzhl#@katK|A{LrFBcovNjwKv8F;G`eBqUOHU6Hrd^4YcyF+eG=XQKgZ^ zWj<6^fBqhze@9mMPC&zscdIt-cXwFw z#kMzYG~v$+yGLJ2^IhWZ*+^6+)Arn(5+nj~)t} zv46>nF>KP}%lDoE@d+S5$~X7LQEk~wU} z8xRCVOWc7@N+oMi-l-%sTHP36`~W|OpL@cG z!|)E@?MD`Os(WOMt2sUz^@JlMkpQMGh+b*805@nT<~^S%Wr#ysdeAGo%}y-4UgCF2 zDc{u~-+&3YSOO^k*rLwIiy7Re0aZqjx`}Ak>yd_}YpS>LDDy4}b4!Uw>V+((7J;vk z#fo1yC6GV=MM4ASl4lC3uMIm~DAbRQ<+XKU<40doIGCRFVc z347rKduxiw3Ms7?5QmT=k|l8yI|{x!luib7&!g5HxJy8~{>(fgV2+Ndm>fMDB%nQf zN;vZXJT6cbyRrSMQ7tD@5i}DK!qGK+(fA{FhCCWn+=><_QW70H?ZZco6y2>-$eMPC zn?6d~eJts3X^yAY55Xx~H4m2o+n zmI$fg?&JIB!K23*XL!sz#nScB!Q#tuVjO`Vc^NW$S9>&*`)9Ezp1V|4?)6_GiA+t}ZYFxk> zGMsr^URa*Fg#rB?_VMBd{8HClk>J`8M*|Nv-l}mH9BW{9Z=_i2`cx^ft6b@X78O|b z$H7-n3YC{lDeB3<1BjV8PUQzCZ{sxXS23Y}^gfvvkgAg&&ewwu(98Th2Je-N>>0xI zgcD;v5*?|n+TCk3Y~_IPUUOD|cI8QJGTUe-&+PalN+7c> z>L^c0(W=eMbguz|EG}f7%wGpSfMQGRe3lB!d^aQinS=b68(kT!4Z5&1 zbys`%>rK;V{#Ul4dgh+zz=68>ScKF^iV2|Net~(G3U)!fdALq-4FJ8%I*LWby7eCg zPoEn@;uS%zEU;VnbQ)e3CxNHn6;hpaZj^oTu~o1Zb#5)jy00A)wSnDw84mbL^yh=^ zeG`%;1eOWM-|b*55-YXN2A!=Uf+VoEgvfSd?fT_2U0&effWaYiWnUB+m z%qn-GddA5Gmux$db{!0PgrT%#BOrZe=x~~kGKps*AP$wpUK)5@KmtS`KZ_1q`v%SI z$z*@Rf!x?5+!nZsxfY@TEVZ_|FUHD6Dc@JLc+M9Cj-m5ABajU-?!|o+hJ2eTfkqVJ zM`B*!S`-DoAtUoeXq-_7u>z(R9-RT#V+0!w`8pD!&>$_&#t@2cV9x{Ld@fwbmk*&M zAVlHLPl91F!?a|1I{P42ZA+3UIec7+hb=LA8`$GjY{$-wlq&Kl%rYWZ=@#OF`G7I1 zo`RV-2F$xNmov;?T{IvFc$J$4EbcovTjj5ZVq7+x1Ny_jUU{z>7$K@R2uhQP zkTK-D{}a3mL-gBU#CQJJ?s}c-YYOcf+AA!@j;;Y(qmi7|S&r6Q;lPSe$xuZJdd0ex zg1HCCuEb*vU~&l+F+pk7<*IGTbV@X>z0W~lTPU9iJv8(DOfzsjzfI%{a<$C8DQ3#r z!8!`wO{Qm%GAzuGhy8=Z$HKtMh0b8{axE$we4Ss8MDISN1)}FJ{?VDi%^aE6UQ=On zeK4Nb)`3x=tc3DhU7AZ5#N6u0Bei?zDcuTWflhKTgGkbVaAqFR)p)5ap^5B~6GxPN zhE%cG@8vaH!M`^TMU0W7q>_-o5|a|LwcGw_r4$1g|G8T7ALu<2M3atf$+^3WOM zs%!1j-40?C3V9UR$r+tywaO^;Ua;XgVm9o_R{r)uPH0V3|1gNr*d5_0GMo)}9VE{< zQNr#3psk?z(}f)z<4WAXWhbSJouDdcY9H^s&$8Y)KLN%-FcL2An&Qg1#qpU~Aps$k zSfb9z!*4U8;!A~eFq9mP9@Ng|{A9?;Ym+k5rHg0M7oODhht=1OJXf2z(#LhNCUD+$3!*S$bW3VY%O+?C9M+jp z3uwYSt2z9FRpPkDYdLpkci}suevG!f7`Y7X)G@Cw{-*bzvu-x7-{RQD)ruq=W z{jTNjy)jXN;d^H(4oJ|z;V!(A8;@O_{P(KKzUQdfI?&f0*68XY9GMVuJUn1Iz!`(l zHL{TlT*aHqvQ<&g0%|mit3Pc545k{a91O@C*atH{w6AmY9y|-RLNu6<*gbNF2BeBs zzCLp*8Ntnn(k}_lj8h9`>Cxk_w2G#DQxU!@Z`{?HRF#)9x~0b`_%FZdiYHmUQ+Huh zD+I*qgj{QNapz!tjYLi7BN#|>KJvZe*ke**=jeI+SHgq%ZiKDi?kAVNf~?vgZqN2;s(@_Y8;&{$ zFSC-tp`s-9dQ>ZEJYJBm{GqkMeyg28f=W$#Geqm`^OGeY3k1Q4ZlvGXhkDuejHNNA zC&UZq;J64ESnUt8R!EQCYi6Xgykf1QbYh=;F`P zfucvLv&|8IS8DR3h@_@U=m0)c+c_Bo=Rz^n@Uhv=v{295^D7IbO=&Rx02>)6i0)8p zgRvxP2#%@n?Khe7X3O;(Yf5Q9Qj01UcP`mssj~3yc=rGQBR6s3R%}RecVQpb4`_#V z$*bnZmX)N4k^+oM9K>6eMPq7AcHqdTN!;F1t(3$H{S~2_Zz1i6QafM++)$91;t-?g zQxBGS3lt*0B;!o3)n=7c^}s~-$PY}%YfOf$-}RMIhC+9D$d0_L=+LuZA($xbER@Y+ ziz=sqx*C(2Zb^8U_ZXEow}+7+KRSR@6B4cv8kmYp!L1Z-Bm4ukes(icu3hz263lcN z2NUK3n}3b+_dkK%-;okFRkg%*Nn$J48$BNplQKxu683(QYPO0LqnOeUJkRA@bTU(> z3(&`i_BbikI=*3i^@@-fI5^*O8sz zsNE|k@iqa7O#qnVgZ(}Ofg%X~y9igK`|+JT`x208c~%6V-Qw)oLdiU~<>NIq>*3au zKg5;70$K$x7q2}ZGHjRLd+88?Zc|$~qnm9U+3@AXq5=rnjrMNZ1*2g`24N7!UDd45z~**kcFX z$_mTTf`qZ|lnJ`(RI;f`-7q)%Yg@U&E7O0sgTyLu6jLeL3aX*gM`ryso=du;zM{b} zQ^`)sIYH?kkS*_1ii#4c#rG5L+-t% z*C$CnSqAkB#^eP01O%Uq-DlH(2!>KVop1L;_>6ya!a{(;&bK_iWQ_kBsESz8&++4LS5GK34w*E^TMvHx4WSlMM<>`+1Z5B{{oY0!3 z()I?ga>RB2H|*A)jOGZaI(qp0wLr`GQvTFzz7TCukd+-(%2spiR0EnupJvzle+wPj z5YTIA>D-1zSABH)oz=k`2}eU`Eu{TVtZsG+>4O89C|eb<{n||6f+30iJtkRUu1j&` z(oY6-=67(z)J0Z(u0=5xZCB4!Cd0s1zl;HP_J6w44fx~(4*UH+;<&Skk@Mn!-Cq*O zR&Jb;$Sq7&s);W1BH4YipUVb>D(>?Vh23-0=cfrO0l}}du6O{)epvEBZ>awTvMP;E z8!XWmRpamc#7iYU6|ZAKt(2dQ0jd(4Pm_=FVoZO4?DF=5JY>|}1S<1-zhqTHOYduo zfm$AD7b`+KPWkXP4TT7eC>dM{B)LDDMSZtvRcF{=03mh7tv&a}6ZLlr*Xpz}Kz-I0 zXt}P91y0f=Xih9DG;IdAcwO*w5Y#La$|95P`Ky(Gc%8ZL0lkZe|E!)!Ok`(BSn6~{ z_`KG!~ zq7GTrol(R*mvZqP%)~rdBil8!@$1wl*}r_2W7-HG~i!Y3#x!OaAs$OtOX9q2UwoMOLI+ zkKoeh^rsRp*Kj4}ue*cBS1|{ZSe2KKb>g6AhPXGimuCm+r45rh))g4T5()-zly~=m zocg(6AVR#~;eu{n!!%*(hfxkpl6dm@<_UcmADpr?#SzxySV+;Ag~Q)D#bNn(0At}c zUy-b%9oa^Wtef8*0E-6>B3Q);=g;up0DLlAz^hV50xr#S7~`ZV(=EKF5^Zk7N!sua z;@*m?;Em4+Ah|keG$oAL9UdFx2`V7q5fqrOI$sMIzO+%%%skWi>M{3xbdNSXdpPmH zc-*aBz@xwbZt?t(0KbF~@&oZ&sai!ZLyQej4F@KB!&SqQIA#a5 zuIYNCcNujy>6lcRqEf8xTcDnH^Llf8;3}PKz2fP6jejkJhAAItG^WVIqV!=-QheZN zU-=eV=^VtuZ@J2&Dab-OYXX|D(ABCT>zb9H)6v*Xkqcz2w>3Oy>gA158{O9P6R)uy zXz9Bb612*3Y;s1k#unsYTl)Y?t5_a|Ne&!Nacr+lO6}9@V4L91akpcq&hM+@a BIWPbK literal 0 HcmV?d00001 diff --git a/application/src/test/resources/coap/credentials/coapserverTest.jks b/application/src/test/resources/coap/credentials/coapserverTest.jks new file mode 100644 index 0000000000000000000000000000000000000000..4adf1f4f8942be609d1716a5fd1518fcaff4b354 GIT binary patch literal 1985 zcmezO_TO6u1_mY|W&zXO#i>PQsYUV0sYN9W42+ZSn@V{wutw;a8dw6kIR;IP89>at zfSHMriAjXBf58F)zP295%^Tb||C(OH`X?sBfQyYotIgw_EekV~fuJG30WTYKC<`+W zTTp&}iL;}DoH(zMiJ_69rJ=E@nW;sTIIl5?YYye2XbA>8#vsx_n2jB5CKDsnaArn! zW+w)ge=FwI7cHJ|%eu@(G-FAY+S(^_39D9}-*m-shK^e#)1ssMCD*@~;(nFg_Ta>y z?N&?l;ww~UZ~nf+?Mg&T+Ns^vi=7P|4Y+|0l;vjux|ekU&?X>9m4yf74Q)0?R#tXq zMgv)pARmhui^!?J&Tq_}%!Ic3D{SpD6s_`AZ6;@q>4`zm{)JXZ1vbOGJ|Iycz7{BU%%JhG0S_A&&~Yq`T197 zY4hTPLOi~II-IP((d1XcyzP55uO77GyOzUW$KZZ1mR&Q#$Zd&Y;^IYfvw8Q6xD~d~ zn9kb0%o#>SBA zo4GzRv~S|fxie4jH`m@Jvdgxp$zPmf3wg7^w!(`&fwHGny>~WVZno49+;P<4H^$IiP(QW=J06N6QA`P zTF*B9{%dn%-gZT#bj@raV8{ndb>N)L8WI#5970k?hUR_RWn^TxGJ7x>xB@fSH{RYW zyk(y=8obsWSi4AQsnE;gUGu#ss?VrZYMT3V+dU=)<^wFMx0f%K?4P7r*sQl9Rq3?L ztL7yM&)aI&f4nXD@i;h_6XYDM|+S9Vlm7UkNXN{my2-6?r_%u*9R5Xx>q&*2oM@I!`VC9sVmYJMb zlBxizA~KUxiwP!2%mhr|)Y!utuT$5cU7InT(XmYD%JxZ)hu@u${vPrA_Nxqm3Pv_2 zh1mb06|>5}T>3Qg)&D{HoqqqE>CvxZi%*S$k9~V_GNpPsWV=Aw25)@iqu2Q8X I`>3=P0KnLuSpWb4 literal 0 HcmV?d00001 diff --git a/application/src/test/resources/coap/credentials/server/cert.pem b/application/src/test/resources/coap/credentials/server/cert.pem new file mode 100644 index 0000000000..03eb9e372e --- /dev/null +++ b/application/src/test/resources/coap/credentials/server/cert.pem @@ -0,0 +1,35 @@ +-----BEGIN CERTIFICATE----- +MIIBaDCCAQ2gAwIBAgIUCY+goBAOhowBs7BHs/qXdAX8XFgwCgYIKoZIzj0EAwIw +ETEPMA0GA1UEAwwGUm9vdENBMB4XDTI0MTIxOTEzNTY1OFoXDTM0MTIxNzEzNTY1 +OFowETEPMA0GA1UEAwwGU2VydmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +/qief3Kjnz0FpkQVaKRqJq3kHmCqqs+y1EGYLEZZAqLFvxmv7xoL6muG4Mj8tzqk +Ll94JJuz97hG1FiEZsq7O6NDMEEwCwYDVR0PBAQDAgWgMBMGA1UdJQQMMAoGCCsG +AQUFBwMBMB0GA1UdDgQWBBTK/UPsN0I2ErVPILWKMRV6TSeAmTAKBggqhkjOPQQD +AgNJADBGAiEA8EhlOwvTbwGlxo55UIOJp9LBbCp0BEIWojlu8PzOVSsCIQDlV24S +3BUJVCuMRujO5lTfJLxaSKkOEIgRANwIGi88WA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIBGzCBwgIUP/PGQOKa5EyvsIXNgvv9PNietyEwCgYIKoZIzj0EAwMwEDEOMAwG +A1UEAwwFVFJVU1QwHhcNMjQxMjE5MTM1NjU4WhcNMzQxMjE3MTM1NjU4WjARMQ8w +DQYDVQQDDAZSb290Q0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAT+qJ5/cqOf +PQWmRBVopGomreQeYKqqz7LUQZgsRlkCosW/Ga/vGgvqa4bgyPy3OqQuX3gkm7P3 +uEbUWIRmyrs7MAoGCCqGSM49BAMDA0gAMEUCIQD2DY3UDXbzaIBKrsCtohKlEunH +ip9LkSeYfSKCnfm23gIgA8AEJdunpRmPkilxgy6wZSLLROqDpGDnhnyv8dsR8cc= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIBLTCB1AIUcsuauXAqvIS2RQcNPYysETJUAvwwCgYIKoZIzj0EAwMwIzEhMB8G +A1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTI0MTIxOTEzNTY1OFoX +DTM0MTIxNzEzNTY1OFowEDEOMAwGA1UEAwwFVFJVU1QwWTATBgcqhkjOPQIBBggq +hkjOPQMBBwNCAAT+qJ5/cqOfPQWmRBVopGomreQeYKqqz7LUQZgsRlkCosW/Ga/v +Ggvqa4bgyPy3OqQuX3gkm7P3uEbUWIRmyrs7MAoGCCqGSM49BAMDA0gAMEUCIQCM +DV8sfoArfWiXAUF2LNS3kkHD7sgb91jr2+poEHgBBgIgXf9VeJp3K5jHX6lJwtE8 +nd+jW7T9nhTc/5njHg7xons= +-----END CERTIFICATE----- +-----BEGIN EC PARAMETERS----- +BggqhkjOPQMBBw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIB+Z69so6HqCCWo5VOFxGsLXOlTWIYijOtzt+SeNGrgPoAoGCCqGSM49 +AwEHoUQDQgAE/qief3Kjnz0FpkQVaKRqJq3kHmCqqs+y1EGYLEZZAqLFvxmv7xoL +6muG4Mj8tzqkLl94JJuz97hG1FiEZsq7Ow== +-----END EC PRIVATE KEY----- diff --git a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/CoapServerService.java b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/CoapServerService.java index 0b1ba35709..5f4f1d152b 100644 --- a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/CoapServerService.java +++ b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/CoapServerService.java @@ -17,7 +17,6 @@ package org.thingsboard.server.coapserver; import org.eclipse.californium.core.CoapServer; -import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.concurrent.ConcurrentMap; @@ -25,5 +24,5 @@ public interface CoapServerService { CoapServer getCoapServer() throws UnknownHostException; - ConcurrentMap getDtlsSessionsMap(); + ConcurrentMap getDtlsSessionsMap(); } diff --git a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java index f3d30bfea4..41b10b31a3 100644 --- a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java +++ b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java @@ -78,7 +78,7 @@ public class DefaultCoapServerService implements CoapServerService { } @Override - public ConcurrentMap getDtlsSessionsMap() { + public ConcurrentMap getDtlsSessionsMap() { return tbDtlsCertificateVerifier != null ? tbDtlsCertificateVerifier.getTbCoapDtlsSessionsMap() : null; } diff --git a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsCertificateVerifier.java b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsCertificateVerifier.java index cc7dcad77c..d61d7f279c 100644 --- a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsCertificateVerifier.java +++ b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsCertificateVerifier.java @@ -109,7 +109,8 @@ public class TbCoapDtlsCertificateVerifier implements NewAdvancedCertificateVeri if (msg != null && strCert.equals(msg.getCredentials())) { DeviceProfile deviceProfile = msg.getDeviceProfile(); if (msg.hasDeviceInfo() && deviceProfile != null) { - tbCoapDtlsSessionInMemoryStorage.put(remotePeer, new TbCoapDtlsSessionInfo(msg, deviceProfile)); + TbCoapDtlsSessionKey tbCoapDtlsSessionKey = new TbCoapDtlsSessionKey(remotePeer, msg.getCredentials()); + tbCoapDtlsSessionInMemoryStorage.put(tbCoapDtlsSessionKey, new TbCoapDtlsSessionInfo(msg, deviceProfile)); } break; } @@ -138,7 +139,7 @@ public class TbCoapDtlsCertificateVerifier implements NewAdvancedCertificateVeri public void setResultHandler(HandshakeResultHandler resultHandler) { } - public ConcurrentMap getTbCoapDtlsSessionsMap() { + public ConcurrentMap getTbCoapDtlsSessionsMap() { return tbCoapDtlsSessionInMemoryStorage.getDtlsSessionsMap(); } diff --git a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSessionInMemoryStorage.java b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSessionInMemoryStorage.java index b4101f1763..5ff44561d8 100644 --- a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSessionInMemoryStorage.java +++ b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSessionInMemoryStorage.java @@ -18,7 +18,6 @@ package org.thingsboard.server.coapserver; import lombok.Data; import lombok.extern.slf4j.Slf4j; -import java.net.InetSocketAddress; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -26,7 +25,7 @@ import java.util.concurrent.ConcurrentMap; @Data public class TbCoapDtlsSessionInMemoryStorage { - private final ConcurrentMap dtlsSessionsMap = new ConcurrentHashMap<>(); + private final ConcurrentMap dtlsSessionsMap = new ConcurrentHashMap<>(); private long dtlsSessionInactivityTimeout; private long dtlsSessionReportTimeout; @@ -36,9 +35,9 @@ public class TbCoapDtlsSessionInMemoryStorage { this.dtlsSessionReportTimeout = dtlsSessionReportTimeout; } - public void put(InetSocketAddress remotePeer, TbCoapDtlsSessionInfo dtlsSessionInfo) { - log.trace("DTLS session added to in-memory store: [{}] timestamp: [{}]", remotePeer, dtlsSessionInfo.getLastActivityTime()); - dtlsSessionsMap.putIfAbsent(remotePeer, dtlsSessionInfo); + public void put(TbCoapDtlsSessionKey tbCoapDtlsSessionKey, TbCoapDtlsSessionInfo dtlsSessionInfo) { + log.trace("DTLS session added to in-memory store: [{}] timestamp: [{}]", tbCoapDtlsSessionKey, dtlsSessionInfo.getLastActivityTime()); + dtlsSessionsMap.putIfAbsent(tbCoapDtlsSessionKey, dtlsSessionInfo); } public void evictTimeoutSessions() { diff --git a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSessionKey.java b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSessionKey.java new file mode 100644 index 0000000000..cf3e0b4fec --- /dev/null +++ b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSessionKey.java @@ -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. + */ +package org.thingsboard.server.coapserver; + +import java.net.InetSocketAddress; +import java.util.Objects; + +public record TbCoapDtlsSessionKey(InetSocketAddress peerAddress, String credentials) { + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TbCoapDtlsSessionKey that = (TbCoapDtlsSessionKey) o; + return Objects.equals(peerAddress, that.peerAddress) && + Objects.equals(credentials, that.credentials); + } +} + diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java index 67ae1b9ae7..f151283d37 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java @@ -24,7 +24,10 @@ import org.eclipse.californium.core.observe.ObserveRelation; import org.eclipse.californium.core.server.resources.CoapExchange; import org.eclipse.californium.core.server.resources.Resource; import org.eclipse.californium.core.server.resources.ResourceObserver; +import org.eclipse.californium.elements.EndpointContext; +import org.eclipse.californium.elements.auth.X509CertPath; import org.thingsboard.server.coapserver.CoapServerService; +import org.thingsboard.server.coapserver.TbCoapDtlsSessionKey; import org.thingsboard.server.coapserver.TbCoapDtlsSessionInfo; import org.thingsboard.server.common.adaptor.AdaptorException; import org.thingsboard.server.common.adaptor.JsonConverter; @@ -47,12 +50,14 @@ import org.thingsboard.server.transport.coap.client.CoapClientContext; import org.thingsboard.server.transport.coap.client.TbCoapClientState; import java.net.InetSocketAddress; +import java.util.Base64; import java.util.List; import java.util.Optional; import java.util.Random; import java.util.UUID; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; +import java.security.cert.X509Certificate; import static org.eclipse.californium.elements.DtlsEndpointContext.KEY_SESSION_ID; @@ -65,7 +70,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource { private static final int FEATURE_TYPE_POSITION_CERTIFICATE_REQUEST = 3; private static final int REQUEST_ID_POSITION_CERTIFICATE_REQUEST = 4; - private final ConcurrentMap dtlsSessionsMap; + private final ConcurrentMap dtlsSessionsMap; private final long timeout; private final long piggybackTimeout; private final CoapClientContext clients; @@ -177,11 +182,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource { var dtlsSessionId = request.getSourceContext().get(KEY_SESSION_ID); if (dtlsSessionsMap != null && dtlsSessionId != null && !dtlsSessionId.isEmpty()) { - TbCoapDtlsSessionInfo tbCoapDtlsSessionInfo = dtlsSessionsMap - .computeIfPresent(request.getSourceContext().getPeerAddress(), (dtlsSessionIdStr, dtlsSessionInfo) -> { - dtlsSessionInfo.setLastActivityTime(System.currentTimeMillis()); - return dtlsSessionInfo; - }); + TbCoapDtlsSessionInfo tbCoapDtlsSessionInfo = this.getCoapDtlsSessionInfo(request.getSourceContext()); if (tbCoapDtlsSessionInfo != null) { processRequest(exchange, type, request, tbCoapDtlsSessionInfo.getMsg(), tbCoapDtlsSessionInfo.getDeviceProfile()); } else { @@ -251,7 +252,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource { TransportProtos.SessionInfoProto sessionInfo = clients.getNewSyncSession(clientState); UUID sessionId = toSessionId(sessionInfo); transportService.process(sessionInfo, clientState.getAdaptor().convertToPostAttributes(sessionId, request, - clientState.getConfiguration().getAttributesMsgDescriptor()), + clientState.getConfiguration().getAttributesMsgDescriptor()), new CoapResponseCodeCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR)); } @@ -259,7 +260,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource { TransportProtos.SessionInfoProto sessionInfo = clients.getNewSyncSession(clientState); UUID sessionId = toSessionId(sessionInfo); transportService.process(sessionInfo, clientState.getAdaptor().convertToPostTelemetry(sessionId, request, - clientState.getConfiguration().getTelemetryMsgDescriptor()), + clientState.getConfiguration().getTelemetryMsgDescriptor()), new CoapResponseCodeCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR)); } @@ -458,5 +459,32 @@ public class CoapTransportResource extends AbstractCoapTransportResource { } } + private TbCoapDtlsSessionInfo getCoapDtlsSessionInfo(EndpointContext endpointContext) { + InetSocketAddress peerAddress = endpointContext.getPeerAddress(); + String certPemStr = getCertPem(endpointContext); + TbCoapDtlsSessionKey tbCoapDtlsSessionKey = StringUtils.isNotBlank(certPemStr) ? new TbCoapDtlsSessionKey(peerAddress, certPemStr) : null; + TbCoapDtlsSessionInfo tbCoapDtlsSessionInfo; + if (tbCoapDtlsSessionKey != null) { + tbCoapDtlsSessionInfo = dtlsSessionsMap + .computeIfPresent(tbCoapDtlsSessionKey, (dtlsSessionIdStr, dtlsSessionInfo) -> { + dtlsSessionInfo.setLastActivityTime(System.currentTimeMillis()); + return dtlsSessionInfo; + }); + } else { + tbCoapDtlsSessionInfo = null; + } + return tbCoapDtlsSessionInfo; + } + private String getCertPem(EndpointContext endpointContext) { + try { + X509CertPath certPath = (X509CertPath) endpointContext.getPeerIdentity(); + X509Certificate x509Certificate = (X509Certificate) certPath.getPath().getCertificates().get(0); + return Base64.getEncoder().encodeToString(x509Certificate.getEncoded()); + } catch (Exception e) { + log.error("Failed to get cert PEM: [{}]", endpointContext.getPeerAddress(), e); + return null; + } + } } + From d35bdcc98111a3d001aee36a1fc501eb1f36bba6 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 6 Jan 2025 13:39:37 +0200 Subject: [PATCH 036/108] Clear JS executor dependencies and code --- .../api/jsInvokeMessageProcessor.ts | 52 +- msa/js-executor/api/utils.ts | 4 - msa/js-executor/package.json | 6 - msa/js-executor/queue/kafkaTemplate.ts | 1 - msa/js-executor/yarn.lock | 1615 +---------------- 5 files changed, 26 insertions(+), 1652 deletions(-) diff --git a/msa/js-executor/api/jsInvokeMessageProcessor.ts b/msa/js-executor/api/jsInvokeMessageProcessor.ts index fe18c320bc..a361dedf67 100644 --- a/msa/js-executor/api/jsInvokeMessageProcessor.ts +++ b/msa/js-executor/api/jsInvokeMessageProcessor.ts @@ -18,7 +18,7 @@ import config from 'config'; import { _logger } from '../config/logger'; import { JsExecutor, TbScript } from './jsExecutor'; import { performance } from 'perf_hooks'; -import { isString, parseJsErrorDetails, toUUIDString, UUIDFromBuffer, UUIDToBits, isNotUUID } from './utils'; +import { isString, parseJsErrorDetails, toUUIDString, UUIDFromBuffer, UUIDToBits } from './utils'; import { IQueue } from '../queue/queue.models'; import { JsCompileRequest, @@ -306,26 +306,14 @@ export class JsInvokeMessageProcessor { } private static createCompileResponse(scriptId: string, success: boolean, errorCode?: number, err?: any): JsCompileResponse { - if (isNotUUID(scriptId)) { - return { - errorCode: errorCode, - success: success, - errorDetails: parseJsErrorDetails(err), - scriptIdMSB: "0", - scriptIdLSB: "0", - scriptHash: scriptId - }; - } else { // this is for backward compatibility (to be able to work with tb-node of previous version) - todo: remove in the next release - let scriptIdBits = UUIDToBits(scriptId); - return { - errorCode: errorCode, - success: success, - errorDetails: parseJsErrorDetails(err), - scriptIdMSB: scriptIdBits[0], - scriptIdLSB: scriptIdBits[1], - scriptHash: "" - }; - } + return { + errorCode: errorCode, + success: success, + errorDetails: parseJsErrorDetails(err), + scriptIdMSB: "0", + scriptIdLSB: "0", + scriptHash: scriptId + }; } private static createInvokeResponse(result: string | undefined, success: boolean, errorCode?: number, err?: any): JsInvokeResponse { @@ -338,22 +326,12 @@ export class JsInvokeMessageProcessor { } private static createReleaseResponse(scriptId: string, success: boolean): JsReleaseResponse { - if (isNotUUID(scriptId)) { - return { - success: success, - scriptIdMSB: "0", - scriptIdLSB: "0", - scriptHash: scriptId, - }; - } else { // todo: remove in the next release - let scriptIdBits = UUIDToBits(scriptId); - return { - success: success, - scriptIdMSB: scriptIdBits[0], - scriptIdLSB: scriptIdBits[1], - scriptHash: "" - } - } + return { + success: success, + scriptIdMSB: "0", + scriptIdLSB: "0", + scriptHash: scriptId, + }; } private static getScriptId(request: TbMessage): string { diff --git a/msa/js-executor/api/utils.ts b/msa/js-executor/api/utils.ts index c228d98315..37e6b8224a 100644 --- a/msa/js-executor/api/utils.ts +++ b/msa/js-executor/api/utils.ts @@ -59,10 +59,6 @@ export function parseJsErrorDetails(err: any): string | undefined { return details; } -export function isNotUUID(candidate: string) { - return candidate.length != 36 || !candidate.includes('-'); -} - export function isNotEmptyStr(value: any): boolean { return typeof value === 'string' && value.trim().length > 0; } diff --git a/msa/js-executor/package.json b/msa/js-executor/package.json index a29dc1ef54..350d6029f0 100644 --- a/msa/js-executor/package.json +++ b/msa/js-executor/package.json @@ -13,17 +13,12 @@ "build": "tsc" }, "dependencies": { - "@aws-sdk/client-sqs": "^3.682.0", - "@azure/service-bus": "^7.9.5", - "@google-cloud/pubsub": "^4.8.0", - "amqplib": "^0.10.4", "config": "^3.3.12", "express": "^4.21.1", "js-yaml": "^4.1.0", "kafkajs": "^2.2.4", "long": "^5.2.3", "uuid-parse": "^1.1.0", - "uuid-random": "^1.3.2", "winston": "^3.16.0", "winston-daily-rotate-file": "^5.0.0" }, @@ -36,7 +31,6 @@ ] }, "devDependencies": { - "@types/amqplib": "^0.10.5", "@types/config": "^3.3.5", "@types/express": "~4.17.21", "@types/node": "~20.17.6", diff --git a/msa/js-executor/queue/kafkaTemplate.ts b/msa/js-executor/queue/kafkaTemplate.ts index 309edca61d..659d1fb8b2 100644 --- a/msa/js-executor/queue/kafkaTemplate.ts +++ b/msa/js-executor/queue/kafkaTemplate.ts @@ -35,7 +35,6 @@ import { KeyObject } from 'tls'; import process, { exit, kill } from 'process'; -// TODO: remove dependencies for other queue types export class KafkaTemplate implements IQueue { private logger = _logger(`kafkaTemplate`); diff --git a/msa/js-executor/yarn.lock b/msa/js-executor/yarn.lock index 662703f9be..7008959b89 100644 --- a/msa/js-executor/yarn.lock +++ b/msa/js-executor/yarn.lock @@ -2,604 +2,6 @@ # yarn lockfile v1 -"@acuminous/bitsyntax@^0.1.2": - version "0.1.2" - resolved "https://registry.yarnpkg.com/@acuminous/bitsyntax/-/bitsyntax-0.1.2.tgz#e0b31b9ee7ad1e4dd840c34864327c33d9f1f653" - integrity sha512-29lUK80d1muEQqiUsSo+3A0yP6CdspgC95EnKBMi22Xlwt79i/En4Vr67+cXhU+cZjbti3TgGGC5wy1stIywVQ== - dependencies: - buffer-more-ints "~1.0.0" - debug "^4.3.4" - safe-buffer "~5.1.2" - -"@aws-crypto/sha256-browser@5.2.0": - version "5.2.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz#153895ef1dba6f9fce38af550e0ef58988eb649e" - integrity sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw== - dependencies: - "@aws-crypto/sha256-js" "^5.2.0" - "@aws-crypto/supports-web-crypto" "^5.2.0" - "@aws-crypto/util" "^5.2.0" - "@aws-sdk/types" "^3.222.0" - "@aws-sdk/util-locate-window" "^3.0.0" - "@smithy/util-utf8" "^2.0.0" - tslib "^2.6.2" - -"@aws-crypto/sha256-js@5.2.0", "@aws-crypto/sha256-js@^5.2.0": - version "5.2.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz#c4fdb773fdbed9a664fc1a95724e206cf3860042" - integrity sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA== - dependencies: - "@aws-crypto/util" "^5.2.0" - "@aws-sdk/types" "^3.222.0" - tslib "^2.6.2" - -"@aws-crypto/supports-web-crypto@^5.2.0": - version "5.2.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz#a1e399af29269be08e695109aa15da0a07b5b5fb" - integrity sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg== - dependencies: - tslib "^2.6.2" - -"@aws-crypto/util@^5.2.0": - version "5.2.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-5.2.0.tgz#71284c9cffe7927ddadac793c14f14886d3876da" - integrity sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ== - dependencies: - "@aws-sdk/types" "^3.222.0" - "@smithy/util-utf8" "^2.0.0" - tslib "^2.6.2" - -"@aws-sdk/client-sqs@^3.682.0": - version "3.682.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-sqs/-/client-sqs-3.682.0.tgz#5b714033a36f9934b627ff1891c3aba78624848a" - integrity sha512-93r0i2VwiHiZkcXfWVoxMpyw91Ou0C6gyS7AzPHoZ9ZoXV1VaBFqQ/FmcLzzNa9pwjE6k/Pn7VJMNKBezE8EmQ== - dependencies: - "@aws-crypto/sha256-browser" "5.2.0" - "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/client-sso-oidc" "3.682.0" - "@aws-sdk/client-sts" "3.682.0" - "@aws-sdk/core" "3.679.0" - "@aws-sdk/credential-provider-node" "3.682.0" - "@aws-sdk/middleware-host-header" "3.679.0" - "@aws-sdk/middleware-logger" "3.679.0" - "@aws-sdk/middleware-recursion-detection" "3.679.0" - "@aws-sdk/middleware-sdk-sqs" "3.679.0" - "@aws-sdk/middleware-user-agent" "3.682.0" - "@aws-sdk/region-config-resolver" "3.679.0" - "@aws-sdk/types" "3.679.0" - "@aws-sdk/util-endpoints" "3.679.0" - "@aws-sdk/util-user-agent-browser" "3.679.0" - "@aws-sdk/util-user-agent-node" "3.682.0" - "@smithy/config-resolver" "^3.0.9" - "@smithy/core" "^2.4.8" - "@smithy/fetch-http-handler" "^3.2.9" - "@smithy/hash-node" "^3.0.7" - "@smithy/invalid-dependency" "^3.0.7" - "@smithy/md5-js" "^3.0.7" - "@smithy/middleware-content-length" "^3.0.9" - "@smithy/middleware-endpoint" "^3.1.4" - "@smithy/middleware-retry" "^3.0.23" - "@smithy/middleware-serde" "^3.0.7" - "@smithy/middleware-stack" "^3.0.7" - "@smithy/node-config-provider" "^3.1.8" - "@smithy/node-http-handler" "^3.2.4" - "@smithy/protocol-http" "^4.1.4" - "@smithy/smithy-client" "^3.4.0" - "@smithy/types" "^3.5.0" - "@smithy/url-parser" "^3.0.7" - "@smithy/util-base64" "^3.0.0" - "@smithy/util-body-length-browser" "^3.0.0" - "@smithy/util-body-length-node" "^3.0.0" - "@smithy/util-defaults-mode-browser" "^3.0.23" - "@smithy/util-defaults-mode-node" "^3.0.23" - "@smithy/util-endpoints" "^2.1.3" - "@smithy/util-middleware" "^3.0.7" - "@smithy/util-retry" "^3.0.7" - "@smithy/util-utf8" "^3.0.0" - tslib "^2.6.2" - -"@aws-sdk/client-sso-oidc@3.682.0": - version "3.682.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.682.0.tgz#423d6b3179fe560a515e3b286689414590f3263b" - integrity sha512-ZPZ7Y/r/w3nx/xpPzGSqSQsB090Xk5aZZOH+WBhTDn/pBEuim09BYXCLzvvxb7R7NnuoQdrTJiwimdJAhHl7ZQ== - dependencies: - "@aws-crypto/sha256-browser" "5.2.0" - "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/core" "3.679.0" - "@aws-sdk/credential-provider-node" "3.682.0" - "@aws-sdk/middleware-host-header" "3.679.0" - "@aws-sdk/middleware-logger" "3.679.0" - "@aws-sdk/middleware-recursion-detection" "3.679.0" - "@aws-sdk/middleware-user-agent" "3.682.0" - "@aws-sdk/region-config-resolver" "3.679.0" - "@aws-sdk/types" "3.679.0" - "@aws-sdk/util-endpoints" "3.679.0" - "@aws-sdk/util-user-agent-browser" "3.679.0" - "@aws-sdk/util-user-agent-node" "3.682.0" - "@smithy/config-resolver" "^3.0.9" - "@smithy/core" "^2.4.8" - "@smithy/fetch-http-handler" "^3.2.9" - "@smithy/hash-node" "^3.0.7" - "@smithy/invalid-dependency" "^3.0.7" - "@smithy/middleware-content-length" "^3.0.9" - "@smithy/middleware-endpoint" "^3.1.4" - "@smithy/middleware-retry" "^3.0.23" - "@smithy/middleware-serde" "^3.0.7" - "@smithy/middleware-stack" "^3.0.7" - "@smithy/node-config-provider" "^3.1.8" - "@smithy/node-http-handler" "^3.2.4" - "@smithy/protocol-http" "^4.1.4" - "@smithy/smithy-client" "^3.4.0" - "@smithy/types" "^3.5.0" - "@smithy/url-parser" "^3.0.7" - "@smithy/util-base64" "^3.0.0" - "@smithy/util-body-length-browser" "^3.0.0" - "@smithy/util-body-length-node" "^3.0.0" - "@smithy/util-defaults-mode-browser" "^3.0.23" - "@smithy/util-defaults-mode-node" "^3.0.23" - "@smithy/util-endpoints" "^2.1.3" - "@smithy/util-middleware" "^3.0.7" - "@smithy/util-retry" "^3.0.7" - "@smithy/util-utf8" "^3.0.0" - tslib "^2.6.2" - -"@aws-sdk/client-sso@3.682.0": - version "3.682.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.682.0.tgz#7533f677456d5f79cfcceed44a3481bcd86b560e" - integrity sha512-PYH9RFUMYLFl66HSBq4tIx6fHViMLkhJHTYJoJONpBs+Td+NwVJ895AdLtDsBIhMS0YseCbPpuyjUCJgsUrwUw== - dependencies: - "@aws-crypto/sha256-browser" "5.2.0" - "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/core" "3.679.0" - "@aws-sdk/middleware-host-header" "3.679.0" - "@aws-sdk/middleware-logger" "3.679.0" - "@aws-sdk/middleware-recursion-detection" "3.679.0" - "@aws-sdk/middleware-user-agent" "3.682.0" - "@aws-sdk/region-config-resolver" "3.679.0" - "@aws-sdk/types" "3.679.0" - "@aws-sdk/util-endpoints" "3.679.0" - "@aws-sdk/util-user-agent-browser" "3.679.0" - "@aws-sdk/util-user-agent-node" "3.682.0" - "@smithy/config-resolver" "^3.0.9" - "@smithy/core" "^2.4.8" - "@smithy/fetch-http-handler" "^3.2.9" - "@smithy/hash-node" "^3.0.7" - "@smithy/invalid-dependency" "^3.0.7" - "@smithy/middleware-content-length" "^3.0.9" - "@smithy/middleware-endpoint" "^3.1.4" - "@smithy/middleware-retry" "^3.0.23" - "@smithy/middleware-serde" "^3.0.7" - "@smithy/middleware-stack" "^3.0.7" - "@smithy/node-config-provider" "^3.1.8" - "@smithy/node-http-handler" "^3.2.4" - "@smithy/protocol-http" "^4.1.4" - "@smithy/smithy-client" "^3.4.0" - "@smithy/types" "^3.5.0" - "@smithy/url-parser" "^3.0.7" - "@smithy/util-base64" "^3.0.0" - "@smithy/util-body-length-browser" "^3.0.0" - "@smithy/util-body-length-node" "^3.0.0" - "@smithy/util-defaults-mode-browser" "^3.0.23" - "@smithy/util-defaults-mode-node" "^3.0.23" - "@smithy/util-endpoints" "^2.1.3" - "@smithy/util-middleware" "^3.0.7" - "@smithy/util-retry" "^3.0.7" - "@smithy/util-utf8" "^3.0.0" - tslib "^2.6.2" - -"@aws-sdk/client-sts@3.682.0": - version "3.682.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.682.0.tgz#97ff70ca141aa6ef48a22f14ef9727bd6ae17b03" - integrity sha512-xKuo4HksZ+F8m9DOfx/ZuWNhaPuqZFPwwy0xqcBT6sWH7OAuBjv/fnpOTzyQhpVTWddlf+ECtMAMrxjxuOExGQ== - dependencies: - "@aws-crypto/sha256-browser" "5.2.0" - "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/client-sso-oidc" "3.682.0" - "@aws-sdk/core" "3.679.0" - "@aws-sdk/credential-provider-node" "3.682.0" - "@aws-sdk/middleware-host-header" "3.679.0" - "@aws-sdk/middleware-logger" "3.679.0" - "@aws-sdk/middleware-recursion-detection" "3.679.0" - "@aws-sdk/middleware-user-agent" "3.682.0" - "@aws-sdk/region-config-resolver" "3.679.0" - "@aws-sdk/types" "3.679.0" - "@aws-sdk/util-endpoints" "3.679.0" - "@aws-sdk/util-user-agent-browser" "3.679.0" - "@aws-sdk/util-user-agent-node" "3.682.0" - "@smithy/config-resolver" "^3.0.9" - "@smithy/core" "^2.4.8" - "@smithy/fetch-http-handler" "^3.2.9" - "@smithy/hash-node" "^3.0.7" - "@smithy/invalid-dependency" "^3.0.7" - "@smithy/middleware-content-length" "^3.0.9" - "@smithy/middleware-endpoint" "^3.1.4" - "@smithy/middleware-retry" "^3.0.23" - "@smithy/middleware-serde" "^3.0.7" - "@smithy/middleware-stack" "^3.0.7" - "@smithy/node-config-provider" "^3.1.8" - "@smithy/node-http-handler" "^3.2.4" - "@smithy/protocol-http" "^4.1.4" - "@smithy/smithy-client" "^3.4.0" - "@smithy/types" "^3.5.0" - "@smithy/url-parser" "^3.0.7" - "@smithy/util-base64" "^3.0.0" - "@smithy/util-body-length-browser" "^3.0.0" - "@smithy/util-body-length-node" "^3.0.0" - "@smithy/util-defaults-mode-browser" "^3.0.23" - "@smithy/util-defaults-mode-node" "^3.0.23" - "@smithy/util-endpoints" "^2.1.3" - "@smithy/util-middleware" "^3.0.7" - "@smithy/util-retry" "^3.0.7" - "@smithy/util-utf8" "^3.0.0" - tslib "^2.6.2" - -"@aws-sdk/core@3.679.0": - version "3.679.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.679.0.tgz#102aa1d19db5bdcabefc2dcd044f2fb5d0771568" - integrity sha512-CS6PWGX8l4v/xyvX8RtXnBisdCa5+URzKd0L6GvHChype9qKUVxO/Gg6N/y43Hvg7MNWJt9FBPNWIxUB+byJwg== - dependencies: - "@aws-sdk/types" "3.679.0" - "@smithy/core" "^2.4.8" - "@smithy/node-config-provider" "^3.1.8" - "@smithy/property-provider" "^3.1.7" - "@smithy/protocol-http" "^4.1.4" - "@smithy/signature-v4" "^4.2.0" - "@smithy/smithy-client" "^3.4.0" - "@smithy/types" "^3.5.0" - "@smithy/util-middleware" "^3.0.7" - fast-xml-parser "4.4.1" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-env@3.679.0": - version "3.679.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.679.0.tgz#abf297714b77197a9da0d3d95a0f5687ae28e5b3" - integrity sha512-EdlTYbzMm3G7VUNAMxr9S1nC1qUNqhKlAxFU8E7cKsAe8Bp29CD5HAs3POc56AVo9GC4yRIS+/mtlZSmrckzUA== - dependencies: - "@aws-sdk/core" "3.679.0" - "@aws-sdk/types" "3.679.0" - "@smithy/property-provider" "^3.1.7" - "@smithy/types" "^3.5.0" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-http@3.679.0": - version "3.679.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.679.0.tgz#9fc29f4ec7ab52ecf394288c05295823e818d812" - integrity sha512-ZoKLubW5DqqV1/2a3TSn+9sSKg0T8SsYMt1JeirnuLJF0mCoYFUaWMyvxxKuxPoqvUsaycxKru4GkpJ10ltNBw== - dependencies: - "@aws-sdk/core" "3.679.0" - "@aws-sdk/types" "3.679.0" - "@smithy/fetch-http-handler" "^3.2.9" - "@smithy/node-http-handler" "^3.2.4" - "@smithy/property-provider" "^3.1.7" - "@smithy/protocol-http" "^4.1.4" - "@smithy/smithy-client" "^3.4.0" - "@smithy/types" "^3.5.0" - "@smithy/util-stream" "^3.1.9" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-ini@3.682.0": - version "3.682.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.682.0.tgz#36a68cd8d0ec3b14acf413166dce72a201fcc2bd" - integrity sha512-6eqWeHdK6EegAxqDdiCi215nT3QZPwukgWAYuVxNfJ/5m0/P7fAzF+D5kKVgByUvGJEbq/FEL8Fw7OBe64AA+g== - dependencies: - "@aws-sdk/core" "3.679.0" - "@aws-sdk/credential-provider-env" "3.679.0" - "@aws-sdk/credential-provider-http" "3.679.0" - "@aws-sdk/credential-provider-process" "3.679.0" - "@aws-sdk/credential-provider-sso" "3.682.0" - "@aws-sdk/credential-provider-web-identity" "3.679.0" - "@aws-sdk/types" "3.679.0" - "@smithy/credential-provider-imds" "^3.2.4" - "@smithy/property-provider" "^3.1.7" - "@smithy/shared-ini-file-loader" "^3.1.8" - "@smithy/types" "^3.5.0" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-node@3.682.0": - version "3.682.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.682.0.tgz#4ec1ebd00dcacb46ae76747b23ebf7bda04808bd" - integrity sha512-HSmDqZcBVZrTctHCT9m++vdlDfJ1ARI218qmZa+TZzzOFNpKWy6QyHMEra45GB9GnkkMmV6unoDSPMuN0AqcMg== - dependencies: - "@aws-sdk/credential-provider-env" "3.679.0" - "@aws-sdk/credential-provider-http" "3.679.0" - "@aws-sdk/credential-provider-ini" "3.682.0" - "@aws-sdk/credential-provider-process" "3.679.0" - "@aws-sdk/credential-provider-sso" "3.682.0" - "@aws-sdk/credential-provider-web-identity" "3.679.0" - "@aws-sdk/types" "3.679.0" - "@smithy/credential-provider-imds" "^3.2.4" - "@smithy/property-provider" "^3.1.7" - "@smithy/shared-ini-file-loader" "^3.1.8" - "@smithy/types" "^3.5.0" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-process@3.679.0": - version "3.679.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.679.0.tgz#a06b5193cdad2c14382708bcd44d487af52b11dc" - integrity sha512-u/p4TV8kQ0zJWDdZD4+vdQFTMhkDEJFws040Gm113VHa/Xo1SYOjbpvqeuFoz6VmM0bLvoOWjxB9MxnSQbwKpQ== - dependencies: - "@aws-sdk/core" "3.679.0" - "@aws-sdk/types" "3.679.0" - "@smithy/property-provider" "^3.1.7" - "@smithy/shared-ini-file-loader" "^3.1.8" - "@smithy/types" "^3.5.0" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-sso@3.682.0": - version "3.682.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.682.0.tgz#aa7e3ffdac82bfc14fc0cf136cec3152f863a63a" - integrity sha512-h7IH1VsWgV6YAJSWWV6y8uaRjGqLY3iBpGZlXuTH/c236NMLaNv+WqCBLeBxkFGUb2WeQ+FUPEJDCD69rgLIkg== - dependencies: - "@aws-sdk/client-sso" "3.682.0" - "@aws-sdk/core" "3.679.0" - "@aws-sdk/token-providers" "3.679.0" - "@aws-sdk/types" "3.679.0" - "@smithy/property-provider" "^3.1.7" - "@smithy/shared-ini-file-loader" "^3.1.8" - "@smithy/types" "^3.5.0" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-web-identity@3.679.0": - version "3.679.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.679.0.tgz#5871c44e5846e7c93810fd033224c00493db65a3" - integrity sha512-a74tLccVznXCaBefWPSysUcLXYJiSkeUmQGtalNgJ1vGkE36W5l/8czFiiowdWdKWz7+x6xf0w+Kjkjlj42Ung== - dependencies: - "@aws-sdk/core" "3.679.0" - "@aws-sdk/types" "3.679.0" - "@smithy/property-provider" "^3.1.7" - "@smithy/types" "^3.5.0" - tslib "^2.6.2" - -"@aws-sdk/middleware-host-header@3.679.0": - version "3.679.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.679.0.tgz#1eabe42250c57a9e28742dd04786781573faad1a" - integrity sha512-y176HuQ8JRY3hGX8rQzHDSbCl9P5Ny9l16z4xmaiLo+Qfte7ee4Yr3yaAKd7GFoJ3/Mhud2XZ37fR015MfYl2w== - dependencies: - "@aws-sdk/types" "3.679.0" - "@smithy/protocol-http" "^4.1.4" - "@smithy/types" "^3.5.0" - tslib "^2.6.2" - -"@aws-sdk/middleware-logger@3.679.0": - version "3.679.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.679.0.tgz#cb0f205ddb5341d8327fc9ca1897bf06526c1896" - integrity sha512-0vet8InEj7nvIvGKk+ch7bEF5SyZ7Us9U7YTEgXPrBNStKeRUsgwRm0ijPWWd0a3oz2okaEwXsFl7G/vI0XiEA== - dependencies: - "@aws-sdk/types" "3.679.0" - "@smithy/types" "^3.5.0" - tslib "^2.6.2" - -"@aws-sdk/middleware-recursion-detection@3.679.0": - version "3.679.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.679.0.tgz#3542de5baa466abffbfe5ee485fd87f60d5f917e" - integrity sha512-sQoAZFsQiW/LL3DfKMYwBoGjYDEnMbA9WslWN8xneCmBAwKo6IcSksvYs23PP8XMIoBGe2I2J9BSr654XWygTQ== - dependencies: - "@aws-sdk/types" "3.679.0" - "@smithy/protocol-http" "^4.1.4" - "@smithy/types" "^3.5.0" - tslib "^2.6.2" - -"@aws-sdk/middleware-sdk-sqs@3.679.0": - version "3.679.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-sqs/-/middleware-sdk-sqs-3.679.0.tgz#d2ccda366b3808081d331b1f2410797cd621431a" - integrity sha512-GjOpT9GRMH6n3Rm9ZsRsrIbLxBPE3/L1KMkIn2uZj14uqz1pdE4ALCN9b9ZkPN+L//rsUrYqtd9gq9Hn9c2FJw== - dependencies: - "@aws-sdk/types" "3.679.0" - "@smithy/smithy-client" "^3.4.0" - "@smithy/types" "^3.5.0" - "@smithy/util-hex-encoding" "^3.0.0" - "@smithy/util-utf8" "^3.0.0" - tslib "^2.6.2" - -"@aws-sdk/middleware-user-agent@3.682.0": - version "3.682.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.682.0.tgz#07d75723bce31e65a29ad0934347537e50e3536e" - integrity sha512-7TyvYR9HdGH1/Nq0eeApUTM4izB6rExiw87khVYuJwZHr6FmvIL1FsOVFro/4WlXa0lg4LiYOm/8H8dHv+fXTg== - dependencies: - "@aws-sdk/core" "3.679.0" - "@aws-sdk/types" "3.679.0" - "@aws-sdk/util-endpoints" "3.679.0" - "@smithy/core" "^2.4.8" - "@smithy/protocol-http" "^4.1.4" - "@smithy/types" "^3.5.0" - tslib "^2.6.2" - -"@aws-sdk/region-config-resolver@3.679.0": - version "3.679.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.679.0.tgz#d205dbaea8385aaf05e637fb7cb095c60bc708be" - integrity sha512-Ybx54P8Tg6KKq5ck7uwdjiKif7n/8g1x+V0V9uTjBjRWqaIgiqzXwKWoPj6NCNkE7tJNtqI4JrNxp/3S3HvmRw== - dependencies: - "@aws-sdk/types" "3.679.0" - "@smithy/node-config-provider" "^3.1.8" - "@smithy/types" "^3.5.0" - "@smithy/util-config-provider" "^3.0.0" - "@smithy/util-middleware" "^3.0.7" - tslib "^2.6.2" - -"@aws-sdk/token-providers@3.679.0": - version "3.679.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.679.0.tgz#7ec462d93941dd3cfdc245104ad32971f6ebc4f6" - integrity sha512-1/+Zso/x2jqgutKixYFQEGli0FELTgah6bm7aB+m2FAWH4Hz7+iMUsazg6nSWm714sG9G3h5u42Dmpvi9X6/hA== - dependencies: - "@aws-sdk/types" "3.679.0" - "@smithy/property-provider" "^3.1.7" - "@smithy/shared-ini-file-loader" "^3.1.8" - "@smithy/types" "^3.5.0" - tslib "^2.6.2" - -"@aws-sdk/types@3.679.0", "@aws-sdk/types@^3.222.0": - version "3.679.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.679.0.tgz#3737bb0f190add9e788b838a24cd5d8106dbed4f" - integrity sha512-NwVq8YvInxQdJ47+zz4fH3BRRLC6lL+WLkvr242PVBbUOLRyK/lkwHlfiKUoeVIMyK5NF+up6TRg71t/8Bny6Q== - dependencies: - "@smithy/types" "^3.5.0" - tslib "^2.6.2" - -"@aws-sdk/util-endpoints@3.679.0": - version "3.679.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.679.0.tgz#b249ad8b4289e634cb5dfb3873a70b7aecbf323f" - integrity sha512-YL6s4Y/1zC45OvddvgE139fjeWSKKPgLlnfrvhVL7alNyY9n7beR4uhoDpNrt5mI6sn9qiBF17790o+xLAXjjg== - dependencies: - "@aws-sdk/types" "3.679.0" - "@smithy/types" "^3.5.0" - "@smithy/util-endpoints" "^2.1.3" - tslib "^2.6.2" - -"@aws-sdk/util-locate-window@^3.0.0": - version "3.679.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-locate-window/-/util-locate-window-3.679.0.tgz#8d5898624691e12ccbad839e103562002bbec85e" - integrity sha512-zKTd48/ZWrCplkXpYDABI74rQlbR0DNHs8nH95htfSLj9/mWRSwaGptoxwcihaq/77vi/fl2X3y0a1Bo8bt7RA== - dependencies: - tslib "^2.6.2" - -"@aws-sdk/util-user-agent-browser@3.679.0": - version "3.679.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.679.0.tgz#bbaa5a8771c8a16388cd3cd934bb84a641ce907d" - integrity sha512-CusSm2bTBG1kFypcsqU8COhnYc6zltobsqs3nRrvYqYaOqtMnuE46K4XTWpnzKgwDejgZGOE+WYyprtAxrPvmQ== - dependencies: - "@aws-sdk/types" "3.679.0" - "@smithy/types" "^3.5.0" - bowser "^2.11.0" - tslib "^2.6.2" - -"@aws-sdk/util-user-agent-node@3.682.0": - version "3.682.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.682.0.tgz#a493d2afb160c5cd4ab0520f929e9b7a2b36f74e" - integrity sha512-so5s+j0gPoTS0HM4HPL+G0ajk0T6cQAg8JXzRgvyiQAxqie+zGCZAV3VuVeMNWMVbzsgZl0pYZaatPFTLG/AxA== - dependencies: - "@aws-sdk/middleware-user-agent" "3.682.0" - "@aws-sdk/types" "3.679.0" - "@smithy/node-config-provider" "^3.1.8" - "@smithy/types" "^3.5.0" - tslib "^2.6.2" - -"@azure/abort-controller@^1.0.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@azure/abort-controller/-/abort-controller-1.1.0.tgz#788ee78457a55af8a1ad342acb182383d2119249" - integrity sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw== - dependencies: - tslib "^2.2.0" - -"@azure/abort-controller@^2.0.0": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@azure/abort-controller/-/abort-controller-2.1.2.tgz#42fe0ccab23841d9905812c58f1082d27784566d" - integrity sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA== - dependencies: - tslib "^2.6.2" - -"@azure/core-amqp@^4.1.1": - version "4.3.2" - resolved "https://registry.yarnpkg.com/@azure/core-amqp/-/core-amqp-4.3.2.tgz#689ea6c363c09a4298a10226b3794f456e3460a1" - integrity sha512-I8sI81E0o38zYjdXcM8cnnvveM6X3f90zqi51zSuD+ZX96P6ZsW8jEXpd/1E7aEUG+MuFGnsEx0iPPS53Lpfnw== - dependencies: - "@azure/abort-controller" "^2.0.0" - "@azure/core-auth" "^1.7.2" - "@azure/core-util" "^1.9.0" - "@azure/logger" "^1.1.2" - buffer "^6.0.3" - events "^3.3.0" - process "^0.11.10" - rhea "^3.0.0" - rhea-promise "^3.0.0" - tslib "^2.6.2" - -"@azure/core-auth@^1.3.0", "@azure/core-auth@^1.4.0", "@azure/core-auth@^1.7.2", "@azure/core-auth@^1.8.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.9.0.tgz#ac725b03fabe3c892371065ee9e2041bee0fd1ac" - integrity sha512-FPwHpZywuyasDSLMqJ6fhbOK3TqUdviZNF8OqRGA4W5Ewib2lEEZ+pBsYcBa88B2NGO/SEnYPGhyBqNlE8ilSw== - dependencies: - "@azure/abort-controller" "^2.0.0" - "@azure/core-util" "^1.11.0" - tslib "^2.6.2" - -"@azure/core-client@^1.0.0": - version "1.9.2" - resolved "https://registry.yarnpkg.com/@azure/core-client/-/core-client-1.9.2.tgz#6fc69cee2816883ab6c5cdd653ee4f2ff9774f74" - integrity sha512-kRdry/rav3fUKHl/aDLd/pDLcB+4pOFwPPTVEExuMyaI5r+JBbMWqRbCY1pn5BniDaU3lRxO9eaQ1AmSMehl/w== - dependencies: - "@azure/abort-controller" "^2.0.0" - "@azure/core-auth" "^1.4.0" - "@azure/core-rest-pipeline" "^1.9.1" - "@azure/core-tracing" "^1.0.0" - "@azure/core-util" "^1.6.1" - "@azure/logger" "^1.0.0" - tslib "^2.6.2" - -"@azure/core-paging@^1.4.0": - version "1.6.2" - resolved "https://registry.yarnpkg.com/@azure/core-paging/-/core-paging-1.6.2.tgz#40d3860dc2df7f291d66350b2cfd9171526433e7" - integrity sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA== - dependencies: - tslib "^2.6.2" - -"@azure/core-rest-pipeline@^1.1.0", "@azure/core-rest-pipeline@^1.9.1": - version "1.17.0" - resolved "https://registry.yarnpkg.com/@azure/core-rest-pipeline/-/core-rest-pipeline-1.17.0.tgz#55dafa1093553c549ed6d8dbca69aa505c7b3aa3" - integrity sha512-62Vv8nC+uPId3j86XJ0WI+sBf0jlqTqPUFCBNrGtlaUeQUIXWV/D8GE5A1d+Qx8H7OQojn2WguC8kChD6v0shA== - dependencies: - "@azure/abort-controller" "^2.0.0" - "@azure/core-auth" "^1.8.0" - "@azure/core-tracing" "^1.0.1" - "@azure/core-util" "^1.9.0" - "@azure/logger" "^1.0.0" - http-proxy-agent "^7.0.0" - https-proxy-agent "^7.0.0" - tslib "^2.6.2" - -"@azure/core-tracing@^1.0.0", "@azure/core-tracing@^1.0.1": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.2.0.tgz#7be5d53c3522d639cf19042cbcdb19f71bc35ab2" - integrity sha512-UKTiEJPkWcESPYJz3X5uKRYyOcJD+4nYph+KpfdPRnQJVrZfk0KJgdnaAWKfhsBBtAf/D58Az4AvCJEmWgIBAg== - dependencies: - tslib "^2.6.2" - -"@azure/core-util@^1.1.1", "@azure/core-util@^1.11.0", "@azure/core-util@^1.6.1", "@azure/core-util@^1.9.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.11.0.tgz#f530fc67e738aea872fbdd1cc8416e70219fada7" - integrity sha512-DxOSLua+NdpWoSqULhjDyAZTXFdP/LKkqtYuxxz1SCN289zk3OG8UOpnCQAz/tygyACBtWp/BoO72ptK7msY8g== - dependencies: - "@azure/abort-controller" "^2.0.0" - tslib "^2.6.2" - -"@azure/core-xml@^1.0.0": - version "1.4.4" - resolved "https://registry.yarnpkg.com/@azure/core-xml/-/core-xml-1.4.4.tgz#a8656751943bf492762f758d147d33dfcd933d9e" - integrity sha512-J4FYAqakGXcbfeZjwjMzjNcpcH4E+JtEBv+xcV1yL0Ydn/6wbQfeFKTCHh9wttAi0lmajHw7yBbHPRG+YHckZQ== - dependencies: - fast-xml-parser "^4.4.1" - tslib "^2.6.2" - -"@azure/logger@^1.0.0", "@azure/logger@^1.1.2": - version "1.1.4" - resolved "https://registry.yarnpkg.com/@azure/logger/-/logger-1.1.4.tgz#223cbf2b424dfa66478ce9a4f575f59c6f379768" - integrity sha512-4IXXzcCdLdlXuCG+8UKEwLA1T1NHqUfanhXYHiQTn+6sfWCZXduqbtXDGceg3Ce5QxTGo7EqmbV6Bi+aqKuClQ== - dependencies: - tslib "^2.6.2" - -"@azure/service-bus@^7.9.5": - version "7.9.5" - resolved "https://registry.yarnpkg.com/@azure/service-bus/-/service-bus-7.9.5.tgz#ad5a6f3caf9e3269c5e838d7b15ecd030e2d8d33" - integrity sha512-R5Af+4jtZZII2snLomaddMyElFtTCBRZp2qERPlP8PuISLU87eFYFM7xWzxjNd0yeiyQUBkamx/ZhOC8eWhCHA== - dependencies: - "@azure/abort-controller" "^1.0.0" - "@azure/core-amqp" "^4.1.1" - "@azure/core-auth" "^1.3.0" - "@azure/core-client" "^1.0.0" - "@azure/core-paging" "^1.4.0" - "@azure/core-rest-pipeline" "^1.1.0" - "@azure/core-tracing" "^1.0.0" - "@azure/core-util" "^1.1.1" - "@azure/core-xml" "^1.0.0" - "@azure/logger" "^1.0.0" - "@types/is-buffer" "^2.0.0" - buffer "^6.0.0" - is-buffer "^2.0.3" - jssha "^3.1.0" - long "^5.2.0" - process "^0.11.10" - rhea-promise "^3.0.0" - tslib "^2.2.0" - "@babel/generator@7.18.2": version "7.18.2" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.2.tgz#33873d6f89b21efe2da63fe554460f3df1c5880d" @@ -662,67 +64,6 @@ enabled "2.0.x" kuler "^2.0.0" -"@google-cloud/paginator@^5.0.0": - version "5.0.2" - resolved "https://registry.yarnpkg.com/@google-cloud/paginator/-/paginator-5.0.2.tgz#86ad773266ce9f3b82955a8f75e22cd012ccc889" - integrity sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg== - dependencies: - arrify "^2.0.0" - extend "^3.0.2" - -"@google-cloud/precise-date@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@google-cloud/precise-date/-/precise-date-4.0.0.tgz#e179893a3ad628b17a6fabdfcc9d468753aac11a" - integrity sha512-1TUx3KdaU3cN7nfCdNf+UVqA/PSX29Cjcox3fZZBtINlRrXVTmUkQnCKv2MbBUbCopbK4olAT1IHl76uZyCiVA== - -"@google-cloud/projectify@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@google-cloud/projectify/-/projectify-4.0.0.tgz#d600e0433daf51b88c1fa95ac7f02e38e80a07be" - integrity sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA== - -"@google-cloud/promisify@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@google-cloud/promisify/-/promisify-4.0.0.tgz#a906e533ebdd0f754dca2509933334ce58b8c8b1" - integrity sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g== - -"@google-cloud/pubsub@^4.8.0": - version "4.8.0" - resolved "https://registry.yarnpkg.com/@google-cloud/pubsub/-/pubsub-4.8.0.tgz#b51acb0c75c6975deeaa7a68d0b56a5d736bb048" - integrity sha512-H9S4i5mAeQg5A4MZox8XfWnoxlMehlIn8QHWZ3iOj7Kz/yaHufsI5JtSGaezjZv+wF4elur5Yycygnl6pWHSyg== - dependencies: - "@google-cloud/paginator" "^5.0.0" - "@google-cloud/precise-date" "^4.0.0" - "@google-cloud/projectify" "^4.0.0" - "@google-cloud/promisify" "^4.0.0" - "@opentelemetry/api" "~1.9.0" - "@opentelemetry/semantic-conventions" "~1.26.0" - arrify "^2.0.0" - extend "^3.0.2" - google-auth-library "^9.3.0" - google-gax "^4.3.3" - heap-js "^2.2.0" - is-stream-ended "^0.1.4" - lodash.snakecase "^4.1.1" - p-defer "^3.0.0" - -"@grpc/grpc-js@^1.10.9": - version "1.12.2" - resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.12.2.tgz#97eda82dd49bb9c24eaf6434ea8d7de446e95aac" - integrity sha512-bgxdZmgTrJZX50OjyVwz3+mNEnCTNkh3cIqGPWVNeW9jX6bn1ZkU80uPd+67/ZpIJIjRQ9qaHCjhavyoWYxumg== - dependencies: - "@grpc/proto-loader" "^0.7.13" - "@js-sdsl/ordered-map" "^4.4.2" - -"@grpc/proto-loader@^0.7.13": - version "0.7.13" - resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.7.13.tgz#f6a44b2b7c9f7b609f5748c6eac2d420e37670cf" - integrity sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw== - dependencies: - lodash.camelcase "^4.3.0" - long "^5.0.0" - protobufjs "^7.2.5" - yargs "^17.7.2" - "@jridgewell/gen-mapping@^0.3.0": version "0.3.5" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" @@ -763,11 +104,6 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@js-sdsl/ordered-map@^4.4.2": - version "4.4.2" - resolved "https://registry.yarnpkg.com/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz#9299f82874bab9e4c7f9c48d865becbfe8d6907c" - integrity sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw== - "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -789,487 +125,6 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@opentelemetry/api@~1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe" - integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg== - -"@opentelemetry/semantic-conventions@~1.26.0": - version "1.26.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.26.0.tgz#42da14476529ca86d0af4c11f58910f242a0a232" - integrity sha512-U9PJlOswJPSgQVPI+XEuNLElyFWkb0hAiMg+DExD9V0St03X2lPHGMdxMY/LrVmoukuIpXJ12oyrOtEZ4uXFkw== - -"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" - integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== - -"@protobufjs/base64@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" - integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== - -"@protobufjs/codegen@^2.0.4": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" - integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== - -"@protobufjs/eventemitter@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" - integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== - -"@protobufjs/fetch@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" - integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== - dependencies: - "@protobufjs/aspromise" "^1.1.1" - "@protobufjs/inquire" "^1.1.0" - -"@protobufjs/float@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" - integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== - -"@protobufjs/inquire@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" - integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== - -"@protobufjs/path@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" - integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== - -"@protobufjs/pool@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" - integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== - -"@protobufjs/utf8@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" - integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== - -"@smithy/abort-controller@^3.1.6": - version "3.1.6" - resolved "https://registry.yarnpkg.com/@smithy/abort-controller/-/abort-controller-3.1.6.tgz#d9de97b85ca277df6ffb9ee7cd83d5da793ee6de" - integrity sha512-0XuhuHQlEqbNQZp7QxxrFTdVWdwxch4vjxYgfInF91hZFkPxf9QDrdQka0KfxFMPqLNzSw0b95uGTrLliQUavQ== - dependencies: - "@smithy/types" "^3.6.0" - tslib "^2.6.2" - -"@smithy/config-resolver@^3.0.10", "@smithy/config-resolver@^3.0.9": - version "3.0.10" - resolved "https://registry.yarnpkg.com/@smithy/config-resolver/-/config-resolver-3.0.10.tgz#d9529d9893e5fae1f14cb1ffd55517feb6d7e50f" - integrity sha512-Uh0Sz9gdUuz538nvkPiyv1DZRX9+D15EKDtnQP5rYVAzM/dnYk3P8cg73jcxyOitPgT3mE3OVj7ky7sibzHWkw== - dependencies: - "@smithy/node-config-provider" "^3.1.9" - "@smithy/types" "^3.6.0" - "@smithy/util-config-provider" "^3.0.0" - "@smithy/util-middleware" "^3.0.8" - tslib "^2.6.2" - -"@smithy/core@^2.4.8", "@smithy/core@^2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@smithy/core/-/core-2.5.1.tgz#7f635b76778afca845bcb401d36f22fa37712f15" - integrity sha512-DujtuDA7BGEKExJ05W5OdxCoyekcKT3Rhg1ZGeiUWaz2BJIWXjZmsG/DIP4W48GHno7AQwRsaCb8NcBgH3QZpg== - dependencies: - "@smithy/middleware-serde" "^3.0.8" - "@smithy/protocol-http" "^4.1.5" - "@smithy/types" "^3.6.0" - "@smithy/util-body-length-browser" "^3.0.0" - "@smithy/util-middleware" "^3.0.8" - "@smithy/util-stream" "^3.2.1" - "@smithy/util-utf8" "^3.0.0" - tslib "^2.6.2" - -"@smithy/credential-provider-imds@^3.2.4", "@smithy/credential-provider-imds@^3.2.5": - version "3.2.5" - resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.5.tgz#dbfd849a4a7ebd68519cd9fc35f78d091e126d0a" - integrity sha512-4FTQGAsuwqTzVMmiRVTn0RR9GrbRfkP0wfu/tXWVHd2LgNpTY0uglQpIScXK4NaEyXbB3JmZt8gfVqO50lP8wg== - dependencies: - "@smithy/node-config-provider" "^3.1.9" - "@smithy/property-provider" "^3.1.8" - "@smithy/types" "^3.6.0" - "@smithy/url-parser" "^3.0.8" - tslib "^2.6.2" - -"@smithy/fetch-http-handler@^3.2.9": - version "3.2.9" - resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.9.tgz#8d5199c162a37caa37a8b6848eefa9ca58221a0b" - integrity sha512-hYNVQOqhFQ6vOpenifFME546f0GfJn2OiQ3M0FDmuUu8V/Uiwy2wej7ZXxFBNqdx0R5DZAqWM1l6VRhGz8oE6A== - dependencies: - "@smithy/protocol-http" "^4.1.4" - "@smithy/querystring-builder" "^3.0.7" - "@smithy/types" "^3.5.0" - "@smithy/util-base64" "^3.0.0" - tslib "^2.6.2" - -"@smithy/fetch-http-handler@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-4.0.0.tgz#3763cb5178745ed630ed5bc3beb6328abdc31f36" - integrity sha512-MLb1f5tbBO2X6K4lMEKJvxeLooyg7guq48C2zKr4qM7F2Gpkz4dc+hdSgu77pCJ76jVqFBjZczHYAs6dp15N+g== - dependencies: - "@smithy/protocol-http" "^4.1.5" - "@smithy/querystring-builder" "^3.0.8" - "@smithy/types" "^3.6.0" - "@smithy/util-base64" "^3.0.0" - tslib "^2.6.2" - -"@smithy/hash-node@^3.0.7": - version "3.0.8" - resolved "https://registry.yarnpkg.com/@smithy/hash-node/-/hash-node-3.0.8.tgz#f451cc342f74830466b0b39bf985dc3022634065" - integrity sha512-tlNQYbfpWXHimHqrvgo14DrMAgUBua/cNoz9fMYcDmYej7MAmUcjav/QKQbFc3NrcPxeJ7QClER4tWZmfwoPng== - dependencies: - "@smithy/types" "^3.6.0" - "@smithy/util-buffer-from" "^3.0.0" - "@smithy/util-utf8" "^3.0.0" - tslib "^2.6.2" - -"@smithy/invalid-dependency@^3.0.7": - version "3.0.8" - resolved "https://registry.yarnpkg.com/@smithy/invalid-dependency/-/invalid-dependency-3.0.8.tgz#4d381a4c24832371ade79e904a72c173c9851e5f" - integrity sha512-7Qynk6NWtTQhnGTTZwks++nJhQ1O54Mzi7fz4PqZOiYXb4Z1Flpb2yRvdALoggTS8xjtohWUM+RygOtB30YL3Q== - dependencies: - "@smithy/types" "^3.6.0" - tslib "^2.6.2" - -"@smithy/is-array-buffer@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz#f84f0d9f9a36601a9ca9381688bd1b726fd39111" - integrity sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA== - dependencies: - tslib "^2.6.2" - -"@smithy/is-array-buffer@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz#9a95c2d46b8768946a9eec7f935feaddcffa5e7a" - integrity sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ== - dependencies: - tslib "^2.6.2" - -"@smithy/md5-js@^3.0.7": - version "3.0.8" - resolved "https://registry.yarnpkg.com/@smithy/md5-js/-/md5-js-3.0.8.tgz#837e54094007e87bf5196e11eca453d1c1e83a26" - integrity sha512-LwApfTK0OJ/tCyNUXqnWCKoE2b4rDSr4BJlDAVCkiWYeHESr+y+d5zlAanuLW6fnitVJRD/7d9/kN/ZM9Su4mA== - dependencies: - "@smithy/types" "^3.6.0" - "@smithy/util-utf8" "^3.0.0" - tslib "^2.6.2" - -"@smithy/middleware-content-length@^3.0.9": - version "3.0.10" - resolved "https://registry.yarnpkg.com/@smithy/middleware-content-length/-/middleware-content-length-3.0.10.tgz#738266f6d81436d7e3a86bea931bc64e04ae7dbf" - integrity sha512-T4dIdCs1d/+/qMpwhJ1DzOhxCZjZHbHazEPJWdB4GDi2HjIZllVzeBEcdJUN0fomV8DURsgOyrbEUzg3vzTaOg== - dependencies: - "@smithy/protocol-http" "^4.1.5" - "@smithy/types" "^3.6.0" - tslib "^2.6.2" - -"@smithy/middleware-endpoint@^3.1.4", "@smithy/middleware-endpoint@^3.2.1": - version "3.2.1" - resolved "https://registry.yarnpkg.com/@smithy/middleware-endpoint/-/middleware-endpoint-3.2.1.tgz#b9ee42d29d8f3a266883d293c4d6a586f7b60979" - integrity sha512-wWO3xYmFm6WRW8VsEJ5oU6h7aosFXfszlz3Dj176pTij6o21oZnzkCLzShfmRaaCHDkBXWBdO0c4sQAvLFP6zA== - dependencies: - "@smithy/core" "^2.5.1" - "@smithy/middleware-serde" "^3.0.8" - "@smithy/node-config-provider" "^3.1.9" - "@smithy/shared-ini-file-loader" "^3.1.9" - "@smithy/types" "^3.6.0" - "@smithy/url-parser" "^3.0.8" - "@smithy/util-middleware" "^3.0.8" - tslib "^2.6.2" - -"@smithy/middleware-retry@^3.0.23": - version "3.0.25" - resolved "https://registry.yarnpkg.com/@smithy/middleware-retry/-/middleware-retry-3.0.25.tgz#a6b1081fc1a0991ffe1d15e567e76198af01f37c" - integrity sha512-m1F70cPaMBML4HiTgCw5I+jFNtjgz5z5UdGnUbG37vw6kh4UvizFYjqJGHvicfgKMkDL6mXwyPp5mhZg02g5sg== - dependencies: - "@smithy/node-config-provider" "^3.1.9" - "@smithy/protocol-http" "^4.1.5" - "@smithy/service-error-classification" "^3.0.8" - "@smithy/smithy-client" "^3.4.2" - "@smithy/types" "^3.6.0" - "@smithy/util-middleware" "^3.0.8" - "@smithy/util-retry" "^3.0.8" - tslib "^2.6.2" - uuid "^9.0.1" - -"@smithy/middleware-serde@^3.0.7", "@smithy/middleware-serde@^3.0.8": - version "3.0.8" - resolved "https://registry.yarnpkg.com/@smithy/middleware-serde/-/middleware-serde-3.0.8.tgz#a46d10dba3c395be0d28610d55c89ff8c07c0cd3" - integrity sha512-Xg2jK9Wc/1g/MBMP/EUn2DLspN8LNt+GMe7cgF+Ty3vl+Zvu+VeZU5nmhveU+H8pxyTsjrAkci8NqY6OuvZnjA== - dependencies: - "@smithy/types" "^3.6.0" - tslib "^2.6.2" - -"@smithy/middleware-stack@^3.0.7", "@smithy/middleware-stack@^3.0.8": - version "3.0.8" - resolved "https://registry.yarnpkg.com/@smithy/middleware-stack/-/middleware-stack-3.0.8.tgz#f1c7d9c7fe8280c6081141c88f4a76875da1fc43" - integrity sha512-d7ZuwvYgp1+3682Nx0MD3D/HtkmZd49N3JUndYWQXfRZrYEnCWYc8BHcNmVsPAp9gKvlurdg/mubE6b/rPS9MA== - dependencies: - "@smithy/types" "^3.6.0" - tslib "^2.6.2" - -"@smithy/node-config-provider@^3.1.8", "@smithy/node-config-provider@^3.1.9": - version "3.1.9" - resolved "https://registry.yarnpkg.com/@smithy/node-config-provider/-/node-config-provider-3.1.9.tgz#d27ba8e4753f1941c24ed0af824dbc6c492f510a" - integrity sha512-qRHoah49QJ71eemjuS/WhUXB+mpNtwHRWQr77J/m40ewBVVwvo52kYAmb7iuaECgGTTcYxHS4Wmewfwy++ueew== - dependencies: - "@smithy/property-provider" "^3.1.8" - "@smithy/shared-ini-file-loader" "^3.1.9" - "@smithy/types" "^3.6.0" - tslib "^2.6.2" - -"@smithy/node-http-handler@^3.2.4", "@smithy/node-http-handler@^3.2.5": - version "3.2.5" - resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-3.2.5.tgz#ad9d9ba1528bf0d4a655135e978ecc14b3df26a2" - integrity sha512-PkOwPNeKdvX/jCpn0A8n9/TyoxjGZB8WVoJmm9YzsnAgggTj4CrjpRHlTQw7dlLZ320n1mY1y+nTRUDViKi/3w== - dependencies: - "@smithy/abort-controller" "^3.1.6" - "@smithy/protocol-http" "^4.1.5" - "@smithy/querystring-builder" "^3.0.8" - "@smithy/types" "^3.6.0" - tslib "^2.6.2" - -"@smithy/property-provider@^3.1.7", "@smithy/property-provider@^3.1.8": - version "3.1.8" - resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-3.1.8.tgz#b1c5a3949effbb9772785ad7ddc5b4b235b10fbe" - integrity sha512-ukNUyo6rHmusG64lmkjFeXemwYuKge1BJ8CtpVKmrxQxc6rhUX0vebcptFA9MmrGsnLhwnnqeH83VTU9hwOpjA== - dependencies: - "@smithy/types" "^3.6.0" - tslib "^2.6.2" - -"@smithy/protocol-http@^4.1.4", "@smithy/protocol-http@^4.1.5": - version "4.1.5" - resolved "https://registry.yarnpkg.com/@smithy/protocol-http/-/protocol-http-4.1.5.tgz#a1f397440f299b6a5abeed6866957fecb1bf5013" - integrity sha512-hsjtwpIemmCkm3ZV5fd/T0bPIugW1gJXwZ/hpuVubt2hEUApIoUTrf6qIdh9MAWlw0vjMrA1ztJLAwtNaZogvg== - dependencies: - "@smithy/types" "^3.6.0" - tslib "^2.6.2" - -"@smithy/querystring-builder@^3.0.7", "@smithy/querystring-builder@^3.0.8": - version "3.0.8" - resolved "https://registry.yarnpkg.com/@smithy/querystring-builder/-/querystring-builder-3.0.8.tgz#0d845be53aa624771c518d1412881236ce12ed4f" - integrity sha512-btYxGVqFUARbUrN6VhL9c3dnSviIwBYD9Rz1jHuN1hgh28Fpv2xjU1HeCeDJX68xctz7r4l1PBnFhGg1WBBPuA== - dependencies: - "@smithy/types" "^3.6.0" - "@smithy/util-uri-escape" "^3.0.0" - tslib "^2.6.2" - -"@smithy/querystring-parser@^3.0.8": - version "3.0.8" - resolved "https://registry.yarnpkg.com/@smithy/querystring-parser/-/querystring-parser-3.0.8.tgz#057a8e2d301eea8eac7071923100ba38a824d7df" - integrity sha512-BtEk3FG7Ks64GAbt+JnKqwuobJNX8VmFLBsKIwWr1D60T426fGrV2L3YS5siOcUhhp6/Y6yhBw1PSPxA5p7qGg== - dependencies: - "@smithy/types" "^3.6.0" - tslib "^2.6.2" - -"@smithy/service-error-classification@^3.0.8": - version "3.0.8" - resolved "https://registry.yarnpkg.com/@smithy/service-error-classification/-/service-error-classification-3.0.8.tgz#265ad2573b972f6c7bdd1ad6c5155a88aeeea1c4" - integrity sha512-uEC/kCCFto83bz5ZzapcrgGqHOh/0r69sZ2ZuHlgoD5kYgXJEThCoTuw/y1Ub3cE7aaKdznb+jD9xRPIfIwD7g== - dependencies: - "@smithy/types" "^3.6.0" - -"@smithy/shared-ini-file-loader@^3.1.8", "@smithy/shared-ini-file-loader@^3.1.9": - version "3.1.9" - resolved "https://registry.yarnpkg.com/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.9.tgz#1b77852b5bb176445e1d80333fa3f739313a4928" - integrity sha512-/+OsJRNtoRbtsX0UpSgWVxFZLsJHo/4sTr+kBg/J78sr7iC+tHeOvOJrS5hCpVQ6sWBbhWLp1UNiuMyZhE6pmA== - dependencies: - "@smithy/types" "^3.6.0" - tslib "^2.6.2" - -"@smithy/signature-v4@^4.2.0": - version "4.2.1" - resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-4.2.1.tgz#a918fd7d99af9f60aa07617506fa54be408126ee" - integrity sha512-NsV1jF4EvmO5wqmaSzlnTVetemBS3FZHdyc5CExbDljcyJCEEkJr8ANu2JvtNbVg/9MvKAWV44kTrGS+Pi4INg== - dependencies: - "@smithy/is-array-buffer" "^3.0.0" - "@smithy/protocol-http" "^4.1.5" - "@smithy/types" "^3.6.0" - "@smithy/util-hex-encoding" "^3.0.0" - "@smithy/util-middleware" "^3.0.8" - "@smithy/util-uri-escape" "^3.0.0" - "@smithy/util-utf8" "^3.0.0" - tslib "^2.6.2" - -"@smithy/smithy-client@^3.4.0", "@smithy/smithy-client@^3.4.2": - version "3.4.2" - resolved "https://registry.yarnpkg.com/@smithy/smithy-client/-/smithy-client-3.4.2.tgz#a6e3ed98330ce170cf482e765bd0c21e0fde8ae4" - integrity sha512-dxw1BDxJiY9/zI3cBqfVrInij6ShjpV4fmGHesGZZUiP9OSE/EVfdwdRz0PgvkEvrZHpsj2htRaHJfftE8giBA== - dependencies: - "@smithy/core" "^2.5.1" - "@smithy/middleware-endpoint" "^3.2.1" - "@smithy/middleware-stack" "^3.0.8" - "@smithy/protocol-http" "^4.1.5" - "@smithy/types" "^3.6.0" - "@smithy/util-stream" "^3.2.1" - tslib "^2.6.2" - -"@smithy/types@^3.5.0", "@smithy/types@^3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@smithy/types/-/types-3.6.0.tgz#03a52bfd62ee4b7b2a1842c8ae3ada7a0a5ff3a4" - integrity sha512-8VXK/KzOHefoC65yRgCn5vG1cysPJjHnOVt9d0ybFQSmJgQj152vMn4EkYhGuaOmnnZvCPav/KnYyE6/KsNZ2w== - dependencies: - tslib "^2.6.2" - -"@smithy/url-parser@^3.0.7", "@smithy/url-parser@^3.0.8": - version "3.0.8" - resolved "https://registry.yarnpkg.com/@smithy/url-parser/-/url-parser-3.0.8.tgz#8057d91d55ba8df97d74576e000f927b42da9e18" - integrity sha512-4FdOhwpTW7jtSFWm7SpfLGKIBC9ZaTKG5nBF0wK24aoQKQyDIKUw3+KFWCQ9maMzrgTJIuOvOnsV2lLGW5XjTg== - dependencies: - "@smithy/querystring-parser" "^3.0.8" - "@smithy/types" "^3.6.0" - tslib "^2.6.2" - -"@smithy/util-base64@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@smithy/util-base64/-/util-base64-3.0.0.tgz#f7a9a82adf34e27a72d0719395713edf0e493017" - integrity sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ== - dependencies: - "@smithy/util-buffer-from" "^3.0.0" - "@smithy/util-utf8" "^3.0.0" - tslib "^2.6.2" - -"@smithy/util-body-length-browser@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz#86ec2f6256310b4845a2f064e2f571c1ca164ded" - integrity sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ== - dependencies: - tslib "^2.6.2" - -"@smithy/util-body-length-node@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz#99a291bae40d8932166907fe981d6a1f54298a6d" - integrity sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA== - dependencies: - tslib "^2.6.2" - -"@smithy/util-buffer-from@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz#6fc88585165ec73f8681d426d96de5d402021e4b" - integrity sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA== - dependencies: - "@smithy/is-array-buffer" "^2.2.0" - tslib "^2.6.2" - -"@smithy/util-buffer-from@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz#559fc1c86138a89b2edaefc1e6677780c24594e3" - integrity sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA== - dependencies: - "@smithy/is-array-buffer" "^3.0.0" - tslib "^2.6.2" - -"@smithy/util-config-provider@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz#62c6b73b22a430e84888a8f8da4b6029dd5b8efe" - integrity sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ== - dependencies: - tslib "^2.6.2" - -"@smithy/util-defaults-mode-browser@^3.0.23": - version "3.0.25" - resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.25.tgz#ef9b84272d1db23503ff155f9075a4543ab6dab7" - integrity sha512-fRw7zymjIDt6XxIsLwfJfYUfbGoO9CmCJk6rjJ/X5cd20+d2Is7xjU5Kt/AiDt6hX8DAf5dztmfP5O82gR9emA== - dependencies: - "@smithy/property-provider" "^3.1.8" - "@smithy/smithy-client" "^3.4.2" - "@smithy/types" "^3.6.0" - bowser "^2.11.0" - tslib "^2.6.2" - -"@smithy/util-defaults-mode-node@^3.0.23": - version "3.0.25" - resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.25.tgz#c16fe3995c8e90ae318e336178392173aebe1e37" - integrity sha512-H3BSZdBDiVZGzt8TG51Pd2FvFO0PAx/A0mJ0EH8a13KJ6iUCdYnw/Dk/MdC1kTd0eUuUGisDFaxXVXo4HHFL1g== - dependencies: - "@smithy/config-resolver" "^3.0.10" - "@smithy/credential-provider-imds" "^3.2.5" - "@smithy/node-config-provider" "^3.1.9" - "@smithy/property-provider" "^3.1.8" - "@smithy/smithy-client" "^3.4.2" - "@smithy/types" "^3.6.0" - tslib "^2.6.2" - -"@smithy/util-endpoints@^2.1.3": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@smithy/util-endpoints/-/util-endpoints-2.1.4.tgz#a29134c2b1982442c5fc3be18d9b22796e8eb964" - integrity sha512-kPt8j4emm7rdMWQyL0F89o92q10gvCUa6sBkBtDJ7nV2+P7wpXczzOfoDJ49CKXe5CCqb8dc1W+ZdLlrKzSAnQ== - dependencies: - "@smithy/node-config-provider" "^3.1.9" - "@smithy/types" "^3.6.0" - tslib "^2.6.2" - -"@smithy/util-hex-encoding@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz#32938b33d5bf2a15796cd3f178a55b4155c535e6" - integrity sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ== - dependencies: - tslib "^2.6.2" - -"@smithy/util-middleware@^3.0.7", "@smithy/util-middleware@^3.0.8": - version "3.0.8" - resolved "https://registry.yarnpkg.com/@smithy/util-middleware/-/util-middleware-3.0.8.tgz#372bc7a2845408ad69da039d277fc23c2734d0c6" - integrity sha512-p7iYAPaQjoeM+AKABpYWeDdtwQNxasr4aXQEA/OmbOaug9V0odRVDy3Wx4ci8soljE/JXQo+abV0qZpW8NX0yA== - dependencies: - "@smithy/types" "^3.6.0" - tslib "^2.6.2" - -"@smithy/util-retry@^3.0.7", "@smithy/util-retry@^3.0.8": - version "3.0.8" - resolved "https://registry.yarnpkg.com/@smithy/util-retry/-/util-retry-3.0.8.tgz#9c607c175a4d8a87b5d8ebaf308f6b849e4dc4d0" - integrity sha512-TCEhLnY581YJ+g1x0hapPz13JFqzmh/pMWL2KEFASC51qCfw3+Y47MrTmea4bUE5vsdxQ4F6/KFbUeSz22Q1ow== - dependencies: - "@smithy/service-error-classification" "^3.0.8" - "@smithy/types" "^3.6.0" - tslib "^2.6.2" - -"@smithy/util-stream@^3.1.9", "@smithy/util-stream@^3.2.1": - version "3.2.1" - resolved "https://registry.yarnpkg.com/@smithy/util-stream/-/util-stream-3.2.1.tgz#f3055dc4c8caba8af4e47191ea7e773d0e5a429d" - integrity sha512-R3ufuzJRxSJbE58K9AEnL/uSZyVdHzud9wLS8tIbXclxKzoe09CRohj2xV8wpx5tj7ZbiJaKYcutMm1eYgz/0A== - dependencies: - "@smithy/fetch-http-handler" "^4.0.0" - "@smithy/node-http-handler" "^3.2.5" - "@smithy/types" "^3.6.0" - "@smithy/util-base64" "^3.0.0" - "@smithy/util-buffer-from" "^3.0.0" - "@smithy/util-hex-encoding" "^3.0.0" - "@smithy/util-utf8" "^3.0.0" - tslib "^2.6.2" - -"@smithy/util-uri-escape@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz#e43358a78bf45d50bb736770077f0f09195b6f54" - integrity sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg== - dependencies: - tslib "^2.6.2" - -"@smithy/util-utf8@^2.0.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-2.3.0.tgz#dd96d7640363259924a214313c3cf16e7dd329c5" - integrity sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A== - dependencies: - "@smithy/util-buffer-from" "^2.2.0" - tslib "^2.6.2" - -"@smithy/util-utf8@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-3.0.0.tgz#1a6a823d47cbec1fd6933e5fc87df975286d9d6a" - integrity sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA== - dependencies: - "@smithy/util-buffer-from" "^3.0.0" - tslib "^2.6.2" - -"@tootallnate/once@2": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" - integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== - "@tsconfig/node10@^1.0.7": version "1.0.11" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" @@ -1290,13 +145,6 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== -"@types/amqplib@^0.10.5": - version "0.10.5" - resolved "https://registry.yarnpkg.com/@types/amqplib/-/amqplib-0.10.5.tgz#fd883eddfbd669702a727fa10007b27c4c1e6ec7" - integrity sha512-/cSykxROY7BWwDoi4Y4/jLAuZTshZxd8Ey1QYa/VaXriMotBDoou7V/twJiOSHzU6t1Kp1AHAUXGCgqq+6DNeg== - dependencies: - "@types/node" "*" - "@types/body-parser@*": version "1.19.5" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4" @@ -1305,11 +153,6 @@ "@types/connect" "*" "@types/node" "*" -"@types/caseless@*": - version "0.12.5" - resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.5.tgz#db9468cb1b1b5a925b8f34822f1669df0c5472f5" - integrity sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg== - "@types/config@^3.3.5": version "3.3.5" resolved "https://registry.yarnpkg.com/@types/config/-/config-3.3.5.tgz#91f0a52b10212b9c4254fb0bbc4bedd77cd389b8" @@ -1347,24 +190,12 @@ resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== -"@types/is-buffer@^2.0.0": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@types/is-buffer/-/is-buffer-2.0.2.tgz#3dcd8e21e7d6c2d312d0b9f6cf23bb6ca1ef9f76" - integrity sha512-G6OXy83Va+xEo8XgqAJYOuvOMxeey9xM5XKkvwJNmN8rVdcB+r15HvHsG86hl86JvU0y1aa7Z2ERkNFYWw9ySg== - dependencies: - "@types/node" "*" - -"@types/long@^4.0.0": - version "4.0.2" - resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" - integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== - "@types/mime@^1": version "1.3.5" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== -"@types/node@*", "@types/node@>=13.7.0": +"@types/node@*": version "22.8.7" resolved "https://registry.yarnpkg.com/@types/node/-/node-22.8.7.tgz#04ab7a073d95b4a6ee899f235d43f3c320a976f4" integrity sha512-LidcG+2UeYIWcMuMUpBKOnryBWG/rnmOHQR5apjn8myTQcx3rinFRn7DcIFhMnS0PPFSC6OafdIKEad0lj6U0Q== @@ -1388,16 +219,6 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== -"@types/request@^2.48.8": - version "2.48.12" - resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.12.tgz#0f590f615a10f87da18e9790ac94c29ec4c5ef30" - integrity sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw== - dependencies: - "@types/caseless" "*" - "@types/node" "*" - "@types/tough-cookie" "*" - form-data "^2.5.0" - "@types/send@*": version "0.17.4" resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" @@ -1415,11 +236,6 @@ "@types/node" "*" "@types/send" "*" -"@types/tough-cookie@*": - version "4.0.5" - resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" - integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== - "@types/triple-beam@^1.3.2": version "1.3.5" resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.5.tgz#74fef9ffbaa198eb8b588be029f38b00299caa2c" @@ -1466,23 +282,6 @@ agent-base@6: dependencies: debug "4" -agent-base@^7.0.2, agent-base@^7.1.0: - version "7.1.1" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" - integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== - dependencies: - debug "^4.3.4" - -amqplib@^0.10.4: - version "0.10.4" - resolved "https://registry.yarnpkg.com/amqplib/-/amqplib-0.10.4.tgz#4058c775830c908267dc198969015e0e8d280e70" - integrity sha512-DMZ4eCEjAVdX1II2TfIUpJhfKAuoCeDIo/YyETbfAqehHTXxxs7WOOd+N1Xxr4cKhx12y23zk8/os98FxlZHrw== - dependencies: - "@acuminous/bitsyntax" "^0.1.2" - buffer-more-ints "~1.0.0" - readable-stream "1.x >=1.1.9" - url-parse "~1.5.10" - ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" @@ -1523,21 +322,11 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -arrify@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" - integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== - async@^3.2.3: version "3.2.6" resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== - at-least-node@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" @@ -1548,16 +337,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-js@^1.3.0, base64-js@^1.3.1: +base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -bignumber.js@^9.0.0: - version "9.1.2" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" - integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== - binary-extensions@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" @@ -1590,11 +374,6 @@ body-parser@1.20.3: type-is "~1.6.18" unpipe "1.0.0" -bowser@^2.11.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f" - integrity sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA== - brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -1610,16 +389,6 @@ braces@^3.0.3, braces@~3.0.2: dependencies: fill-range "^7.1.1" -buffer-equal-constant-time@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" - integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== - -buffer-more-ints@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz#ef4f8e2dddbad429ed3828a9c55d44f05c611422" - integrity sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg== - buffer@^5.5.0: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" @@ -1628,7 +397,7 @@ buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" -buffer@^6.0.0, buffer@^6.0.3: +buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== @@ -1689,15 +458,6 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" -cliui@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" - integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.1" - wrap-ansi "^7.0.0" - color-convert@^1.9.3: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -1746,13 +506,6 @@ colorspace@1.1.x: color "^3.1.3" text-hex "1.0.x" -combined-stream@^1.0.6: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -1804,7 +557,7 @@ debug@2.6.9: dependencies: ms "2.0.0" -debug@4, debug@^4, debug@^4.0.0, debug@^4.3.3, debug@^4.3.4: +debug@4, debug@^4: version "4.3.7" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== @@ -1832,11 +585,6 @@ define-data-property@^1.1.4: es-errors "^1.3.0" gopd "^1.0.1" -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== - depd@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" @@ -1864,23 +612,6 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -duplexify@^4.0.0: - version "4.1.3" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-4.1.3.tgz#a07e1c0d0a2c001158563d32592ba58bddb0236f" - integrity sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA== - dependencies: - end-of-stream "^1.4.1" - inherits "^2.0.3" - readable-stream "^3.1.1" - stream-shift "^1.0.2" - -ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" - integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== - dependencies: - safe-buffer "^5.0.1" - ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -1992,11 +723,6 @@ express@^4.21.1: utils-merge "1.0.1" vary "~1.1.2" -extend@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - fast-glob@^3.2.9: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" @@ -2008,20 +734,6 @@ fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-xml-parser@4.4.1: - version "4.4.1" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz#86dbf3f18edf8739326447bcaac31b4ae7f6514f" - integrity sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw== - dependencies: - strnum "^1.0.5" - -fast-xml-parser@^4.4.1: - version "4.5.0" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz#2882b7d01a6825dfdf909638f2de0256351def37" - integrity sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg== - dependencies: - strnum "^1.0.5" - fastq@^1.6.0: version "1.17.1" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" @@ -2066,16 +778,6 @@ fn.name@1.x.x: resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== -form-data@^2.5.0: - version "2.5.2" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.2.tgz#dc653743d1de2fcc340ceea38079daf6e9069fd2" - integrity sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - safe-buffer "^5.2.1" - forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -2128,25 +830,6 @@ function-bind@^1.1.2: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -gaxios@^6.0.0, gaxios@^6.1.1: - version "6.7.1" - resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-6.7.1.tgz#ebd9f7093ede3ba502685e73390248bb5b7f71fb" - integrity sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ== - dependencies: - extend "^3.0.2" - https-proxy-agent "^7.0.1" - is-stream "^2.0.0" - node-fetch "^2.6.9" - uuid "^9.0.1" - -gcp-metadata@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-6.1.0.tgz#9b0dd2b2445258e7597f2024332d20611cbd6b8c" - integrity sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg== - dependencies: - gaxios "^6.0.0" - json-bigint "^1.0.0" - get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -2187,36 +870,6 @@ globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -google-auth-library@^9.3.0: - version "9.14.2" - resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-9.14.2.tgz#92a53ba32b3a9ff9ced8ed34129edb5a7fa7fb52" - integrity sha512-R+FRIfk1GBo3RdlRYWPdwk8nmtVUOn6+BkDomAC46KoU8kzXzE1HLmOasSCbWUByMMAGkknVF0G5kQ69Vj7dlA== - dependencies: - base64-js "^1.3.0" - ecdsa-sig-formatter "^1.0.11" - gaxios "^6.1.1" - gcp-metadata "^6.1.0" - gtoken "^7.0.0" - jws "^4.0.0" - -google-gax@^4.3.3: - version "4.4.1" - resolved "https://registry.yarnpkg.com/google-gax/-/google-gax-4.4.1.tgz#95a9cf7ee7777ac22d1926a45b5f886dd8beecae" - integrity sha512-Phyp9fMfA00J3sZbJxbbB4jC55b7DBjE3F6poyL3wKMEBVKA79q6BGuHcTiM28yOzVql0NDbRL8MLLh8Iwk9Dg== - dependencies: - "@grpc/grpc-js" "^1.10.9" - "@grpc/proto-loader" "^0.7.13" - "@types/long" "^4.0.0" - abort-controller "^3.0.0" - duplexify "^4.0.0" - google-auth-library "^9.3.0" - node-fetch "^2.7.0" - object-hash "^3.0.0" - proto3-json-serializer "^2.0.2" - protobufjs "^7.3.2" - retry-request "^7.0.0" - uuid "^9.0.1" - gopd@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" @@ -2229,14 +882,6 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -gtoken@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-7.1.0.tgz#d61b4ebd10132222817f7222b1e6064bd463fc26" - integrity sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw== - dependencies: - gaxios "^6.0.0" - jws "^4.0.0" - has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -2276,11 +921,6 @@ hasown@^2.0.0, hasown@^2.0.2: dependencies: function-bind "^1.1.2" -heap-js@^2.2.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/heap-js/-/heap-js-2.5.0.tgz#487e268b1733b187ca04eccf52f8387be92b46cb" - integrity sha512-kUGoI3p7u6B41z/dp33G6OaL7J4DRqRYwVmeIlwLClx7yaaAy7hoDExnuejTKtuDwfcatGmddHDEOjf6EyIxtQ== - http-errors@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" @@ -2292,23 +932,6 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" -http-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" - integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== - dependencies: - "@tootallnate/once" "2" - agent-base "6" - debug "4" - -http-proxy-agent@^7.0.0: - version "7.0.2" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" - integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== - dependencies: - agent-base "^7.1.0" - debug "^4.3.4" - https-proxy-agent@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" @@ -2317,14 +940,6 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" -https-proxy-agent@^7.0.0, https-proxy-agent@^7.0.1: - version "7.0.5" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2" - integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw== - dependencies: - agent-base "^7.0.2" - debug "4" - iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -2347,7 +962,7 @@ ignore@^5.2.0: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== -inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: +inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -2382,11 +997,6 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" -is-buffer@^2.0.3: - version "2.0.5" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" - integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== - is-core-module@2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" @@ -2423,21 +1033,11 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-stream-ended@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/is-stream-ended/-/is-stream-ended-0.1.4.tgz#f50224e95e06bce0e356d440a4827cd35b267eda" - integrity sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw== - is-stream@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== - isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -2455,13 +1055,6 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== -json-bigint@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1" - integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ== - dependencies: - bignumber.js "^9.0.0" - json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" @@ -2476,28 +1069,6 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" -jssha@^3.1.0: - version "3.3.1" - resolved "https://registry.yarnpkg.com/jssha/-/jssha-3.3.1.tgz#c5b7fc7fb9aa745461923b87df0e247dd59c7ea8" - integrity sha512-VCMZj12FCFMQYcFLPRm/0lOBbLi8uM2BhXPTqw3U4YAfs4AZfiApOoBLoN8cQE60Z50m1MYMTQVCfgF/KaCVhQ== - -jwa@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/jwa/-/jwa-2.0.0.tgz#a7e9c3f29dae94027ebcaf49975c9345593410fc" - integrity sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA== - dependencies: - buffer-equal-constant-time "1.0.1" - ecdsa-sig-formatter "1.0.11" - safe-buffer "^5.0.1" - -jws@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jws/-/jws-4.0.0.tgz#2d4e8cf6a318ffaa12615e9dec7e86e6c97310f4" - integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg== - dependencies: - jwa "^2.0.0" - safe-buffer "^5.0.1" - kafkajs@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/kafkajs/-/kafkajs-2.2.4.tgz#59e6e16459d87fdf8b64be73970ed5aa42370a5b" @@ -2508,16 +1079,6 @@ kuler@^2.0.0: resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== -lodash.camelcase@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" - integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== - -lodash.snakecase@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d" - integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw== - logform@^2.6.0, logform@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/logform/-/logform-2.6.1.tgz#71403a7d8cae04b2b734147963236205db9b3df0" @@ -2530,7 +1091,7 @@ logform@^2.6.0, logform@^2.6.1: safe-stable-stringify "^2.3.1" triple-beam "^1.3.0" -long@^5.0.0, long@^5.2.0, long@^5.2.3: +long@^5.2.3: version "5.2.3" resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1" integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== @@ -2573,7 +1134,7 @@ mime-db@1.52.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -2647,7 +1208,7 @@ node-abi@^3.3.0: dependencies: semver "^7.3.5" -node-fetch@^2.6.6, node-fetch@^2.6.9, node-fetch@^2.7.0: +node-fetch@^2.6.6: version "2.7.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== @@ -2706,11 +1267,6 @@ one-time@^1.0.0: dependencies: fn.name "1.x.x" -p-defer@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-3.0.0.tgz#d1dceb4ee9b2b604b1d94ffec83760175d4e6f83" - integrity sha512-ugZxsxmtTln604yeYd29EGrNhazN2lywetzpKhfmQjW/VJmhpDmWbiX+h0zL8V91R0UXkhb3KtPmyq9PZw3aYw== - p-is-promise@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-3.0.0.tgz#58e78c7dfe2e163cf2a04ff869e7c1dba64a5971" @@ -2808,31 +1364,6 @@ progress@^2.0.3: resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== -proto3-json-serializer@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz#5b705203b4d58f3880596c95fad64902617529dd" - integrity sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ== - dependencies: - protobufjs "^7.2.5" - -protobufjs@^7.2.5, protobufjs@^7.3.2: - version "7.4.0" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.4.0.tgz#7efe324ce9b3b61c82aae5de810d287bc08a248a" - integrity sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw== - dependencies: - "@protobufjs/aspromise" "^1.1.2" - "@protobufjs/base64" "^1.1.2" - "@protobufjs/codegen" "^2.0.4" - "@protobufjs/eventemitter" "^1.1.0" - "@protobufjs/fetch" "^1.1.0" - "@protobufjs/float" "^1.0.2" - "@protobufjs/inquire" "^1.1.0" - "@protobufjs/path" "^1.1.2" - "@protobufjs/pool" "^1.1.0" - "@protobufjs/utf8" "^1.1.0" - "@types/node" ">=13.7.0" - long "^5.0.0" - proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" @@ -2861,11 +1392,6 @@ qs@6.13.0: dependencies: side-channel "^1.0.6" -querystringify@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" - integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== - queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -2896,16 +1422,6 @@ rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -"readable-stream@1.x >=1.1.9": - version "1.1.14" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - integrity sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - readable-stream@^2.0.0, readable-stream@^2.1.4: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" @@ -2951,11 +1467,6 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== - resolve@^1.22.0: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" @@ -2965,36 +1476,11 @@ resolve@^1.22.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -retry-request@^7.0.0: - version "7.0.2" - resolved "https://registry.yarnpkg.com/retry-request/-/retry-request-7.0.2.tgz#60bf48cfb424ec01b03fca6665dee91d06dd95f3" - integrity sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w== - dependencies: - "@types/request" "^2.48.8" - extend "^3.0.2" - teeny-request "^9.0.0" - reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rhea-promise@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/rhea-promise/-/rhea-promise-3.0.3.tgz#fc68e39019442c5338a3999b5e3e28806f3f10d2" - integrity sha512-a875P5YcMkePSTEWMsnmCQS7Y4v/XvIw7ZoMtJxqtQRZsqSA6PsZxuz4vktyRykPuUgdNsA6F84dS3iEXZoYnQ== - dependencies: - debug "^4.0.0" - rhea "^3.0.0" - tslib "^2.6.0" - -rhea@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/rhea/-/rhea-3.0.3.tgz#38ff144b7f8ca982a67718aa1f5e67bd93075679" - integrity sha512-Y7se0USZQu6dErWSZ7eCmSVTMscyVfz/0+jjhBF7f9PqYfEXdIoQpPkC9Strks6wF9WytuBhn8w8Nz/tmBWpgA== - dependencies: - debug "^4.3.3" - run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -3002,12 +1488,12 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.2.1, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-buffer@~5.1.0, safe-buffer@~5.1.1, safe-buffer@~5.1.2: +safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== @@ -3126,13 +1612,6 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== -stream-events@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/stream-events/-/stream-events-1.0.5.tgz#bbc898ec4df33a4902d892333d47da9bf1c406d5" - integrity sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg== - dependencies: - stubs "^3.0.0" - stream-meter@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/stream-meter/-/stream-meter-1.0.4.tgz#52af95aa5ea760a2491716704dbff90f73afdd1d" @@ -3140,12 +1619,7 @@ stream-meter@^1.0.4: dependencies: readable-stream "^2.1.4" -stream-shift@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.3.tgz#85b8fab4d71010fc3ba8772e8046cc49b8a3864b" - integrity sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ== - -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +string-width@^4.1.0, string-width@^4.2.0: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -3161,11 +1635,6 @@ string_decoder@^1.1.1, string_decoder@^1.3.0: dependencies: safe-buffer "~5.2.0" -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ== - string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -3185,16 +1654,6 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== -strnum@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" - integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== - -stubs@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/stubs/-/stubs-3.0.0.tgz#e8d2ba1fa9c90570303c030b6900f7d5f89abe5b" - integrity sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw== - supports-color@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -3235,17 +1694,6 @@ tar-stream@^2.1.4: inherits "^2.0.3" readable-stream "^3.1.1" -teeny-request@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-9.0.0.tgz#18140de2eb6595771b1b02203312dfad79a4716d" - integrity sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g== - dependencies: - http-proxy-agent "^5.0.0" - https-proxy-agent "^5.0.0" - node-fetch "^2.6.9" - stream-events "^1.0.5" - uuid "^9.0.0" - text-hex@1.0.x: version "1.0.0" resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" @@ -3302,11 +1750,6 @@ ts-node@^10.9.2: v8-compile-cache-lib "^3.0.1" yn "3.1.1" -tslib@^2.2.0, tslib@^2.6.0, tslib@^2.6.2: - version "2.8.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" - integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== - tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -3347,14 +1790,6 @@ unpipe@1.0.0, unpipe@~1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== -url-parse@~1.5.10: - version "1.5.10" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" - integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== - dependencies: - querystringify "^2.1.1" - requires-port "^1.0.0" - util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -3370,16 +1805,6 @@ uuid-parse@^1.1.0: resolved "https://registry.yarnpkg.com/uuid-parse/-/uuid-parse-1.1.0.tgz#7061c5a1384ae0e1f943c538094597e1b5f3a65b" integrity sha512-OdmXxA8rDsQ7YpNVbKSJkNzTw2I+S5WsbMDnCtIWSQaosNAcWtFuI/YK1TjzUI6nbkgiqEyh8gWngfcv8Asd9A== -uuid-random@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/uuid-random/-/uuid-random-1.3.2.tgz#96715edbaef4e84b1dcf5024b00d16f30220e2d0" - integrity sha512-UOzej0Le/UgkbWEO8flm+0y+G+ljUon1QWTEZOq1rnMAsxo2+SckbiZdKzAHHlVh6gJqI1TjC/xwgR50MuCrBQ== - -uuid@^9.0.0, uuid@^9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" - integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== - v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" @@ -3463,11 +1888,6 @@ yargs-parser@^20.2.2: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== -yargs-parser@^21.1.1: - version "21.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" - integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== - yargs@^16.2.0: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" @@ -3481,19 +1901,6 @@ yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^17.7.2: - version "17.7.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" - integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== - dependencies: - cliui "^8.0.1" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" - y18n "^5.0.5" - yargs-parser "^21.1.1" - yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" From 0a969f22b3ab4f00b000435894f20c0033082739 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 6 Jan 2025 15:39:41 +0200 Subject: [PATCH 037/108] Clear JS executor proto models --- msa/js-executor/api/jsExecutor.models.ts | 2 -- msa/js-executor/api/jsInvokeMessageProcessor.ts | 8 ++------ msa/js-executor/api/utils.ts | 7 ------- 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/msa/js-executor/api/jsExecutor.models.ts b/msa/js-executor/api/jsExecutor.models.ts index 3f14079139..182ec5cf23 100644 --- a/msa/js-executor/api/jsExecutor.models.ts +++ b/msa/js-executor/api/jsExecutor.models.ts @@ -16,8 +16,6 @@ export interface TbMessage { - scriptIdMSB: string; // deprecated - scriptIdLSB: string; // deprecated scriptHash: string; } diff --git a/msa/js-executor/api/jsInvokeMessageProcessor.ts b/msa/js-executor/api/jsInvokeMessageProcessor.ts index a361dedf67..f55a1ba468 100644 --- a/msa/js-executor/api/jsInvokeMessageProcessor.ts +++ b/msa/js-executor/api/jsInvokeMessageProcessor.ts @@ -18,7 +18,7 @@ import config from 'config'; import { _logger } from '../config/logger'; import { JsExecutor, TbScript } from './jsExecutor'; import { performance } from 'perf_hooks'; -import { isString, parseJsErrorDetails, toUUIDString, UUIDFromBuffer, UUIDToBits } from './utils'; +import { isString, parseJsErrorDetails, UUIDFromBuffer, UUIDToBits } from './utils'; import { IQueue } from '../queue/queue.models'; import { JsCompileRequest, @@ -310,8 +310,6 @@ export class JsInvokeMessageProcessor { errorCode: errorCode, success: success, errorDetails: parseJsErrorDetails(err), - scriptIdMSB: "0", - scriptIdLSB: "0", scriptHash: scriptId }; } @@ -328,14 +326,12 @@ export class JsInvokeMessageProcessor { private static createReleaseResponse(scriptId: string, success: boolean): JsReleaseResponse { return { success: success, - scriptIdMSB: "0", - scriptIdLSB: "0", scriptHash: scriptId, }; } private static getScriptId(request: TbMessage): string { - return request.scriptHash ? request.scriptHash : toUUIDString(request.scriptIdMSB, request.scriptIdLSB); + return request.scriptHash; } private incrementUseScriptId(scriptId: string) { diff --git a/msa/js-executor/api/utils.ts b/msa/js-executor/api/utils.ts index 37e6b8224a..f044d40f58 100644 --- a/msa/js-executor/api/utils.ts +++ b/msa/js-executor/api/utils.ts @@ -17,13 +17,6 @@ import Long from 'long'; import uuidParse from 'uuid-parse'; -export function toUUIDString(mostSigBits: string, leastSigBits: string): string { - const msbBytes = Long.fromValue(mostSigBits, false).toBytes(false); - const lsbBytes = Long.fromValue(leastSigBits, false).toBytes(false); - const uuidBytes = msbBytes.concat(lsbBytes); - return uuidParse.unparse(uuidBytes as any); -} - export function UUIDFromBuffer(buf: Buffer): string { return uuidParse.unparse(buf); } From d2e949e9eb4a950dee82b0a9fd214650aa41fdf5 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 7 Jan 2025 11:39:28 +0200 Subject: [PATCH 038/108] UI: Added correct page refresh in dev mode for scada editor page --- ui-ngx/angular.json | 3 +- ui-ngx/esbuild/tb-html-fallback-middleware.ts | 32 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 ui-ngx/esbuild/tb-html-fallback-middleware.ts diff --git a/ui-ngx/angular.json b/ui-ngx/angular.json index f605bfee8a..8f98a6cf07 100644 --- a/ui-ngx/angular.json +++ b/ui-ngx/angular.json @@ -179,7 +179,8 @@ "builder": "@angular-builders/custom-esbuild:dev-server", "options": { "buildTarget": "thingsboard:build", - "proxyConfig": "proxy.conf.js" + "proxyConfig": "proxy.conf.js", + "middlewares": ["./esbuild/tb-html-fallback-middleware.ts"] }, "configurations": { "production": { diff --git a/ui-ngx/esbuild/tb-html-fallback-middleware.ts b/ui-ngx/esbuild/tb-html-fallback-middleware.ts new file mode 100644 index 0000000000..bcb86823d3 --- /dev/null +++ b/ui-ngx/esbuild/tb-html-fallback-middleware.ts @@ -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. +/// + +import type { ServerResponse } from 'node:http'; +import type { Connect } from 'vite'; +import type { NextHandleFunction } from 'connect'; + +const tbHtmlFallbackMiddleware: NextHandleFunction = ( + req: Connect.IncomingMessage, + _res: ServerResponse, + next: Connect.NextFunction +) => { + if (/^\/resources\/scada-symbols\/(?:system|tenant)\/[^/]+\.svg$/.test(req.url)) { + req.url = '/'; + } + next(); +} + +export default tbHtmlFallbackMiddleware; From 08b1af3f4262fe84cede20b1776494554ec70b23 Mon Sep 17 00:00:00 2001 From: Artem Barysh Date: Tue, 7 Jan 2025 12:27:35 +0200 Subject: [PATCH 039/108] fix: removed default host value for RabbitMQ node --- .../rule/engine/rabbitmq/TbRabbitMqNodeConfiguration.java | 1 - 1 file changed, 1 deletion(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeConfiguration.java index 177a4f752b..6049ff919d 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeConfiguration.java @@ -44,7 +44,6 @@ public class TbRabbitMqNodeConfiguration implements NodeConfiguration Date: Tue, 7 Jan 2025 13:42:23 +0200 Subject: [PATCH 040/108] Fixed rabbitmq test method --- .../thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java index 8ce71f684c..334fa5b633 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java @@ -103,7 +103,7 @@ public class TbRabbitMqNodeTest { assertThat(config.getExchangeNamePattern()).isEqualTo(""); assertThat(config.getRoutingKeyPattern()).isEqualTo(""); assertThat(config.getMessageProperties()).isNull(); - assertThat(config.getHost()).isEqualTo(ConnectionFactory.DEFAULT_HOST); + assertThat(config.getHost()).isEqualTo(null); assertThat(config.getPort()).isEqualTo(ConnectionFactory.DEFAULT_AMQP_PORT); assertThat(config.getVirtualHost()).isEqualTo(ConnectionFactory.DEFAULT_VHOST); assertThat(config.getUsername()).isEqualTo(ConnectionFactory.DEFAULT_USER); From e5df59456a286842d0b126a31cd1a4b62b3daa05 Mon Sep 17 00:00:00 2001 From: nick Date: Tue, 7 Jan 2025 18:40:19 +0200 Subject: [PATCH 041/108] fix bug: update Observe dynamically and between connections due to profile changes --- .../lwm2m/AbstractLwM2MIntegrationTest.java | 34 +++++++- .../lwm2m/client/LwM2MTestClient.java | 6 ++ .../lwm2m/client/SimpleLwM2MDevice.java | 1 + .../AbstractSecurityLwM2MIntegrationTest.java | 3 +- .../security/sql/PskLwm2mIntegrationTest.java | 81 ++++++++++++++++++- .../uplink/DefaultLwM2mUplinkMsgHandler.java | 2 +- 6 files changed, 123 insertions(+), 4 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index a1fb723681..065c4a2d26 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java @@ -148,13 +148,27 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte " \"attributeLwm2m\": {}\n" + " }"; public static String OBSERVE_ATTRIBUTES_WITH_PARAMS = - " {\n" + " \"keyName\": {\n" + " \"/3_1.2/0/9\": \"batteryLevel\"\n" + " },\n" + " \"observe\": [],\n" + " \"attribute\": [\n" + + " \"/3_1.2/0/9\"\n" + + " ],\n" + + " \"telemetry\": [\n" + + " ],\n" + + " \"attributeLwm2m\": {}\n" + + " }"; + public static String TELEMETRY_WITH_ONE_OBSERVE = + " {\n" + + " \"keyName\": {\n" + + " \"/3_1.2/0/9\": \"batteryLevel\"\n" + + " },\n" + + " \"observe\": [\n" + + " \"/3_1.2/0/9\"\n" + + " ],\n" + + " \"attribute\": [\n" + " ],\n" + " \"telemetry\": [\n" + " \"/3_1.2/0/9\"\n" + @@ -162,6 +176,24 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte " \"attributeLwm2m\": {}\n" + " }"; + public static String TELEMETRY_WITH_MANY_OBSERVE = + " {\n" + + " \"keyName\": {\n" + + " \"/3_1.2/0/9\": \"batteryLevel\",\n" + + " \"/3_1.2/0/20\": \"batteryStatus\"\n" + + " },\n" + + " \"observe\": [\n" + + " \"/3_1.2/0/9\",\n" + + " \"/3_1.2/0/20\"\n" + + " ],\n" + + " \"attribute\": [],\n" + + " \"telemetry\": [\n" + + " \"/3_1.2/0/9\",\n" + + " \"/3_1.2/0/20\"\n" + + " ],\n" + + " \"attributeLwm2m\": {}\n" + + " }"; + public static final String CLIENT_LWM2M_SETTINGS = " {\n" + " \"edrxCycle\": null,\n" + diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java index 387085c511..1c09825e92 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java @@ -458,6 +458,12 @@ public class LwM2MTestClient { } } + public void stop(boolean deregister) { + if (leshanClient != null) { + leshanClient.stop(deregister); + } + } + private void awaitClientAfterStartConnectLw() { LwM2mClient lwM2MClient = this.clientContext.getClientByEndpoint(endpoint); Mockito.doAnswer(invocationOnMock -> null).when(defaultLwM2mUplinkMsgHandlerTest).initAttributes(lwM2MClient, true); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SimpleLwM2MDevice.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SimpleLwM2MDevice.java index a6a850a897..e94a6b4822 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SimpleLwM2MDevice.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SimpleLwM2MDevice.java @@ -93,6 +93,7 @@ public class SimpleLwM2MDevice extends BaseInstanceEnabler implements Destroyabl try { executorService.scheduleWithFixedDelay(() -> { fireResourceChange(9); + fireResourceChange(20); } , 1, 1, TimeUnit.SECONDS); // 2 sec // , 1800000, 1800000, TimeUnit.MILLISECONDS); // 30 MIN diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java index eaade683d8..3f43c59068 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java @@ -196,7 +196,7 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M false); } - protected void basicTestConnection(Security security, Security securityBs, + protected Device basicTestConnection(Security security, Security securityBs, LwM2MDeviceCredentials deviceCredentials, String endpoint, Lwm2mDeviceProfileTransportConfiguration transportConfiguration, @@ -227,6 +227,7 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M return lwM2MTestClient.getClientStates().contains(finishState) || lwM2MTestClient.getClientStates().contains(ON_UPDATE_SUCCESS); }); Assert.assertTrue(lwM2MTestClient.getClientStates().containsAll(expectedStatuses)); + return device; } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java index ac1c0866de..42014a4a75 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java @@ -16,10 +16,13 @@ package org.thingsboard.server.transport.lwm2m.security.sql; import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.client.object.Security; import org.eclipse.leshan.core.util.Hex; +import org.junit.Assert; import org.junit.Test; import org.springframework.test.web.servlet.MvcResult; +import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MDeviceCredentials; import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKClientCredential; @@ -27,7 +30,6 @@ import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTrans import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; import java.nio.charset.StandardCharsets; - import static org.eclipse.leshan.client.object.Security.psk; import static org.eclipse.leshan.client.object.Security.pskBootstrap; import static org.junit.Assert.assertEquals; @@ -37,6 +39,7 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClient import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.BOTH; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.NONE; +@Slf4j public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTest { //Lwm2m only @@ -66,6 +69,82 @@ public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTes ON_REGISTRATION_SUCCESS, true); } + @Test + public void testWithPskConnectLwm2mOneObserveSuccessUpdateProfileManyObserveUpdateRegistrationSuccess() throws Exception { + String clientEndpoint = CLIENT_ENDPOINT_PSK; + String identity = CLIENT_PSK_IDENTITY; + String keyPsk = CLIENT_PSK_KEY; + PSKClientCredential clientCredentials = new PSKClientCredential(); + clientCredentials.setEndpoint(clientEndpoint); + clientCredentials.setIdentity(identity); + clientCredentials.setKey(keyPsk); + Security security = psk(SECURE_URI, + shortServerId, + identity.getBytes(StandardCharsets.UTF_8), + Hex.decodeHex(keyPsk.toCharArray())); + Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(TELEMETRY_WITH_ONE_OBSERVE, getBootstrapServerCredentialsSecure(PSK, NONE)); + LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, null, null, PSK, false); + String awaitAlias = "await on client state (Psk_Lwm2m)"; + Device lwm2mDevice = this.basicTestConnection(security, + null, + deviceCredentials, + clientEndpoint, + transportConfiguration, + awaitAlias, + expectedStatusesRegistrationLwm2mSuccess, + false, + ON_REGISTRATION_SUCCESS, + true); + + awaitObserveReadAll(1, lwm2mDevice.getId().getId().toString()); + DeviceProfile foundDeviceProfile = doGet("/api/deviceProfile/" + lwm2mDevice.getDeviceProfileId().getId().toString(), DeviceProfile.class); + transportConfiguration = getTransportConfiguration(TELEMETRY_WITH_MANY_OBSERVE, getBootstrapServerCredentialsSecure(PSK, NONE)); + foundDeviceProfile.getProfileData().setTransportConfiguration(transportConfiguration); + DeviceProfile lwm2mDeviceProfileManyParams = doPost("/api/deviceProfile", foundDeviceProfile, DeviceProfile.class); + Assert.assertNotNull(lwm2mDeviceProfileManyParams); + awaitObserveReadAll(2, lwm2mDevice.getId().getId().toString()); + awaitUpdateReg(3); + } + @Test + public void testWithPskConnectLwm2mSuccessObserveSuccessUnRegClientUpdateProfileObserveConnectLwm2mSuccessOWithNewObserve() throws Exception { + String clientEndpoint = CLIENT_ENDPOINT_PSK; + String identity = CLIENT_PSK_IDENTITY; + String keyPsk = CLIENT_PSK_KEY; + PSKClientCredential clientCredentials = new PSKClientCredential(); + clientCredentials.setEndpoint(clientEndpoint); + clientCredentials.setIdentity(identity); + clientCredentials.setKey(keyPsk); + Security security = psk(SECURE_URI, + shortServerId, + identity.getBytes(StandardCharsets.UTF_8), + Hex.decodeHex(keyPsk.toCharArray())); + Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(TELEMETRY_WITH_ONE_OBSERVE, getBootstrapServerCredentialsSecure(PSK, NONE)); + LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, null, null, PSK, false); + String awaitAlias = "await on client state (Psk_Lwm2m)"; + Device lwm2mDevice = this.basicTestConnection(security, + null, + deviceCredentials, + clientEndpoint, + transportConfiguration, + awaitAlias, + expectedStatusesRegistrationLwm2mSuccess, + false, + ON_REGISTRATION_SUCCESS, + true); + + awaitObserveReadAll(1, lwm2mDevice.getId().getId().toString()); + lwM2MTestClient.stop(true); + + DeviceProfile foundDeviceProfile = doGet("/api/deviceProfile/" + lwm2mDevice.getDeviceProfileId().getId().toString(), DeviceProfile.class); + transportConfiguration = getTransportConfiguration(TELEMETRY_WITH_MANY_OBSERVE, getBootstrapServerCredentialsSecure(PSK, NONE)); + foundDeviceProfile.getProfileData().setTransportConfiguration(transportConfiguration); + DeviceProfile lwm2mDeviceProfileManyParams = doPost("/api/deviceProfile", foundDeviceProfile, DeviceProfile.class); + Assert.assertNotNull(lwm2mDeviceProfileManyParams); + + lwM2MTestClient.start(true); + awaitObserveReadAll(2, lwm2mDevice.getId().getId().toString()); + awaitUpdateReg(3); + } @Test public void testWithPskConnectLwm2mBadPskKeyByLength_BAD_REQUEST() throws Exception { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java index a79ed43784..d415b8c22a 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java @@ -279,6 +279,7 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl clientContext.unregister(client, registration); SessionInfoProto sessionInfo = client.getSession(); if (sessionInfo != null) { + securityStore.remove(client.getEndpoint(), client.getRegistration().getId()); sessionManager.deregister(sessionInfo); sessionStore.remove(registration.getEndpoint()); log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); @@ -401,7 +402,6 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl .stream().filter(e -> e.getProfileId() != null) .filter(e -> e.getProfileId().equals(deviceProfile.getUuidId())).collect(Collectors.toList()); clients.forEach(client -> { - this.securityStore.remove(client.getEndpoint(), client.getRegistration().getId()); client.onDeviceProfileUpdate(deviceProfile); }); if (clients.size() > 0) { From f1083098116a450cb85970a5953a300d0c1b50ca Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 7 Jan 2025 19:07:33 +0200 Subject: [PATCH 042/108] UI: Move rule node config to TB --- .../engine/action/TbAssignToCustomerNode.java | 1 - .../rule/engine/action/TbClearAlarmNode.java | 1 - .../TbCopyAttributesToEntityViewNode.java | 1 - .../rule/engine/action/TbCreateAlarmNode.java | 1 - .../engine/action/TbCreateRelationNode.java | 1 - .../engine/action/TbDeleteRelationNode.java | 1 - .../rule/engine/action/TbDeviceStateNode.java | 1 - .../rule/engine/action/TbLogNode.java | 1 - .../rule/engine/action/TbMsgCountNode.java | 1 - .../TbSaveToCustomCassandraTableNode.java | 1 - .../action/TbUnassignFromCustomerNode.java | 1 - .../engine/aws/lambda/TbAwsLambdaNode.java | 1 - .../rule/engine/aws/sns/TbSnsNode.java | 1 - .../rule/engine/aws/sqs/TbSqsNode.java | 1 - .../rule/engine/debug/TbMsgGeneratorNode.java | 1 - .../deduplication/TbMsgDeduplicationNode.java | 1 - .../rule/engine/delay/TbMsgDelayNode.java | 1 - .../engine/edge/TbMsgPushToCloudNode.java | 1 - .../rule/engine/edge/TbMsgPushToEdgeNode.java | 1 - .../engine/filter/TbAssetTypeSwitchNode.java | 1 - .../engine/filter/TbCheckAlarmStatusNode.java | 1 - .../engine/filter/TbCheckMessageNode.java | 1 - .../engine/filter/TbCheckRelationNode.java | 1 - .../engine/filter/TbDeviceTypeSwitchNode.java | 1 - .../rule/engine/filter/TbJsFilterNode.java | 1 - .../rule/engine/filter/TbJsSwitchNode.java | 1 - .../engine/filter/TbMsgTypeFilterNode.java | 1 - .../engine/filter/TbMsgTypeSwitchNode.java | 1 - .../filter/TbOriginatorTypeFilterNode.java | 1 - .../filter/TbOriginatorTypeSwitchNode.java | 1 - .../rule/engine/flow/TbAckNode.java | 1 - .../rule/engine/flow/TbCheckpointNode.java | 1 - .../engine/flow/TbRuleChainInputNode.java | 1 - .../engine/flow/TbRuleChainOutputNode.java | 1 - .../rule/engine/gcp/pubsub/TbPubSubNode.java | 1 - .../engine/geo/TbGpsGeofencingActionNode.java | 1 - .../engine/geo/TbGpsGeofencingFilterNode.java | 1 - .../rule/engine/kafka/TbKafkaNode.java | 1 - .../rule/engine/mail/TbMsgToEmailNode.java | 1 - .../rule/engine/mail/TbSendEmailNode.java | 1 - .../rule/engine/math/TbMathNode.java | 1 - .../engine/metadata/CalculateDeltaNode.java | 1 - .../TbFetchDeviceCredentialsNode.java | 1 - .../engine/metadata/TbGetAttributesNode.java | 1 - .../metadata/TbGetCustomerAttributeNode.java | 1 - .../metadata/TbGetCustomerDetailsNode.java | 1 - .../engine/metadata/TbGetDeviceAttrNode.java | 1 - .../metadata/TbGetOriginatorFieldsNode.java | 1 - .../metadata/TbGetRelatedAttributeNode.java | 1 - .../engine/metadata/TbGetTelemetryNode.java | 1 - .../metadata/TbGetTenantAttributeNode.java | 1 - .../metadata/TbGetTenantDetailsNode.java | 1 - .../rule/engine/mqtt/TbMqttNode.java | 1 - .../engine/mqtt/azure/TbAzureIotHubNode.java | 1 - .../notification/TbNotificationNode.java | 1 - .../rule/engine/notification/TbSlackNode.java | 1 - .../engine/profile/TbDeviceProfileNode.java | 1 - .../rule/engine/rabbitmq/TbRabbitMqNode.java | 1 - .../rule/engine/rest/TbRestApiCallNode.java | 1 - .../rest/TbSendRestApiCallReplyNode.java | 1 - .../rule/engine/rpc/TbSendRPCReplyNode.java | 1 - .../rule/engine/rpc/TbSendRPCRequestNode.java | 1 - .../rule/engine/sms/TbSendSmsNode.java | 1 - .../engine/telemetry/TbMsgAttributesNode.java | 1 - .../telemetry/TbMsgDeleteAttributesNode.java | 1 - .../engine/telemetry/TbMsgTimeseriesNode.java | 1 - .../TbSynchronizationBeginNode.java | 1 - .../transaction/TbSynchronizationEndNode.java | 1 - .../transform/TbChangeOriginatorNode.java | 1 - .../rule/engine/transform/TbCopyKeysNode.java | 1 - .../engine/transform/TbDeleteKeysNode.java | 1 - .../rule/engine/transform/TbJsonPathNode.java | 1 - .../engine/transform/TbRenameKeysNode.java | 1 - .../engine/transform/TbSplitArrayMsgNode.java | 1 - .../engine/transform/TbTransformMsgNode.java | 1 - .../assign-customer-config.component.html | 35 + .../assign-customer-config.component.ts | 49 + .../action/attributes-config.component.html | 79 ++ .../action/attributes-config.component.ts | 62 ++ .../action/clear-alarm-config.component.html | 67 ++ .../action/clear-alarm-config.component.ts | 127 +++ .../action/create-alarm-config.component.html | 128 +++ .../action/create-alarm-config.component.ts | 199 ++++ .../create-relation-config.component.html | 100 ++ .../create-relation-config.component.ts | 107 +++ .../delete-attributes-config.component.html | 89 ++ .../delete-attributes-config.component.ts | 88 ++ .../delete-relation-config.component.html | 62 ++ .../delete-relation-config.component.ts | 101 ++ .../device-profile-config.component.html | 32 + .../action/device-profile-config.component.ts | 59 ++ .../action/device-state-config.component.html | 27 + .../action/device-state-config.component.ts | 59 ++ .../action/generator-config.component.html | 117 +++ .../action/generator-config.component.scss | 51 ++ .../action/generator-config.component.ts | 155 ++++ .../gps-geo-action-config.component.html | 195 ++++ .../gps-geo-action-config.component.scss | 20 + .../action/gps-geo-action-config.component.ts | 126 +++ .../action/log-config.component.html | 59 ++ .../rule-node/action/log-config.component.ts | 124 +++ .../math-function-config.component.html | 103 +++ .../math-function-config.component.scss | 41 + .../action/math-function-config.component.ts | 92 ++ .../action/msg-count-config.component.html | 36 + .../action/msg-count-config.component.ts | 45 + .../action/msg-delay-config.component.html | 57 ++ .../action/msg-delay-config.component.ts | 65 ++ .../push-to-cloud-config.component.html | 49 + .../action/push-to-cloud-config.component.ts | 48 + .../action/push-to-edge-config.component.html | 49 + .../action/push-to-edge-config.component.ts | 48 + .../action/rpc-reply-config.component.html | 36 + .../action/rpc-reply-config.component.ts | 45 + .../action/rpc-request-config.component.html | 29 + .../action/rpc-request-config.component.ts | 43 + .../action/rule-node-config-action.module.ts | 132 +++ ...save-to-custom-table-config.component.html | 56 ++ .../save-to-custom-table-config.component.ts | 50 + ...-rest-api-call-reply-config.component.html | 32 + ...nd-rest-api-call-reply-config.component.ts | 44 + .../action/timeseries-config.component.html | 50 + .../action/timeseries-config.component.ts | 45 + .../unassign-customer-config.component.html | 39 + .../unassign-customer-config.component.ts | 72 ++ .../common/alarm-status-select.component.html | 37 + .../common/alarm-status-select.component.scss | 60 ++ .../common/alarm-status-select.component.ts | 83 ++ .../arguments-map-config.component.html | 129 +++ .../arguments-map-config.component.scss | 27 + .../common/arguments-map-config.component.ts | 242 +++++ .../common/credentials-config.component.html | 95 ++ .../common/credentials-config.component.ts | 252 +++++ ...vice-relations-query-config.component.html | 66 ++ ...vice-relations-query-config.component.scss | 20 + ...device-relations-query-config.component.ts | 108 +++ .../common/example-hint.component.html | 29 + .../common/example-hint.component.scss | 31 + .../common/example-hint.component.ts | 32 + .../common/kv-map-config-old.component.html | 70 ++ .../common/kv-map-config-old.component.scss | 59 ++ .../common/kv-map-config-old.component.ts | 198 ++++ .../common/kv-map-config.component.html | 72 ++ .../common/kv-map-config.component.scss | 24 + .../common/kv-map-config.component.ts | 240 +++++ .../math-function-autocomplete.component.html | 43 + .../math-function-autocomplete.component.ts | 152 +++ .../message-types-config.component.html | 71 ++ .../common/message-types-config.component.ts | 239 +++++ .../common/msg-metadata-chip.component.html | 26 + .../common/msg-metadata-chip.component.ts | 96 ++ ...t-message-type-autocomplete.component.html | 50 + ...put-message-type-autocomplete.component.ts | 178 ++++ .../relations-query-config-old.component.html | 45 + .../relations-query-config-old.component.ts | 99 ++ .../relations-query-config.component.html | 61 ++ .../relations-query-config.component.ts | 98 ++ .../common/rule-node-config-common.module.ts | 80 ++ .../common/select-attributes.component.html | 59 ++ .../common/select-attributes.component.ts | 139 +++ .../common/sv-map-config.component.html | 70 ++ .../common/sv-map-config.component.scss | 24 + .../common/sv-map-config.component.ts | 266 ++++++ .../rule-node/empty-config.component.ts | 42 + .../calculate-delta-config.component.html | 92 ++ .../calculate-delta-config.component.ts | 87 ++ .../customer-attributes-config.component.html | 46 + .../customer-attributes-config.component.scss | 23 + .../customer-attributes-config.component.ts | 100 ++ .../device-attributes-config.component.html | 46 + .../device-attributes-config.component.ts | 77 ++ .../entity-details-config.component.html | 35 + .../entity-details-config.component.ts | 87 ++ ...h-device-credentials-config.component.html | 23 + ...tch-device-credentials-config.component.ts | 51 ++ ...emetry-from-database-config.component.html | 182 ++++ ...emetry-from-database-config.component.scss | 66 ++ ...elemetry-from-database-config.component.ts | 205 +++++ ...riginator-attributes-config.component.html | 40 + .../originator-attributes-config.component.ts | 75 ++ .../originator-fields-config.component.html | 41 + .../originator-fields-config.component.ts | 67 ++ .../related-attributes-config.component.html | 62 ++ .../related-attributes-config.component.ts | 160 ++++ .../rule-node-core-enrichment.module.ts | 77 ++ .../tenant-attributes-config.component.html | 45 + .../tenant-attributes-config.component.scss | 21 + .../tenant-attributes-config.component.ts | 91 ++ .../azure-iot-hub-config.component.html | 122 +++ .../azure-iot-hub-config.component.ts | 117 +++ .../external/kafka-config.component.html | 111 +++ .../external/kafka-config.component.ts | 79 ++ .../external/lambda-config.component.html | 115 +++ .../external/lambda-config.component.ts | 50 + .../external/mqtt-config.component.html | 85 ++ .../external/mqtt-config.component.scss | 20 + .../external/mqtt-config.component.ts | 71 ++ .../notification-config.component.html | 34 + .../external/notification-config.component.ts | 48 + .../external/pubsub-config.component.html | 53 ++ .../external/pubsub-config.component.ts | 47 + .../external/rabbit-mq-config.component.html | 96 ++ .../external/rabbit-mq-config.component.ts | 64 ++ .../rest-api-call-config.component.html | 130 +++ .../rest-api-call-config.component.ts | 95 ++ .../rule-node-config-external.module.ts | 91 ++ .../external/send-email-config.component.html | 116 +++ .../external/send-email-config.component.ts | 95 ++ .../external/send-sms-config.component.html | 43 + .../external/send-sms-config.component.ts | 61 ++ .../external/slack-config.component.html | 49 + .../external/slack-config.component.scss | 43 + .../external/slack-config.component.ts | 64 ++ .../external/sns-config.component.html | 48 + .../external/sns-config.component.ts | 46 + .../external/sqs-config.component.html | 76 ++ .../external/sqs-config.component.ts | 54 ++ .../filter/check-alarm-status.component.html | 29 + .../filter/check-alarm-status.component.ts | 52 ++ .../check-message-config.component.html | 45 + .../filter/check-message-config.component.ts | 78 ++ .../check-relation-config.component.html | 55 ++ .../check-relation-config.component.scss | 20 + .../filter/check-relation-config.component.ts | 77 ++ .../gps-geo-filter-config.component.html | 126 +++ .../gps-geo-filter-config.component.scss | 20 + .../filter/gps-geo-filter-config.component.ts | 121 +++ .../filter/message-type-config.component.html | 24 + .../filter/message-type-config.component.ts | 51 ++ .../originator-type-config.component.html | 31 + .../originator-type-config.component.ts | 65 ++ .../filter/rule-node-config-filter.module.ts | 69 ++ .../filter/script-config.component.html | 57 ++ .../filter/script-config.component.ts | 128 +++ .../filter/switch-config.component.html | 57 ++ .../filter/switch-config.component.ts | 130 +++ .../flow/rule-chain-input.component.html | 33 + .../flow/rule-chain-input.component.ts | 48 + .../flow/rule-chain-output.component.html | 20 + .../flow/rule-chain-output.component.ts | 42 + .../flow/rule-node-config-flow.module.ts | 43 + .../rule-node/rule-node-config.models.ts | 862 ++++++++++++++++++ .../rule-node/rule-node-config.module.ts | 75 ++ .../change-originator-config.component.html | 66 ++ .../change-originator-config.component.ts | 83 ++ .../transform/copy-keys-config.component.html | 36 + .../transform/copy-keys-config.component.ts | 73 ++ .../deduplication-config.component.html | 100 ++ .../deduplication-config.component.ts | 79 ++ .../delete-keys-config.component.html | 35 + .../transform/delete-keys-config.component.ts | 74 ++ .../node-json-path-config.component.html | 25 + .../node-json-path-config.component.ts | 44 + .../rename-keys-config.component.html | 40 + .../rename-keys-config.component.scss | 27 + .../transform/rename-keys-config.component.ts | 73 ++ .../rule-node-config-transform.module.ts | 70 ++ .../transform/script-config.component.html | 59 ++ .../transform/script-config.component.ts | 128 +++ .../transform/to-email-config.component.html | 139 +++ .../transform/to-email-config.component.scss | 29 + .../transform/to-email-config.component.ts | 98 ++ .../rulechain/rule-node-config.component.ts | 14 +- .../home/pages/rulechain/rulechain.module.ts | 4 +- .../src/app/shared/models/rule-node.models.ts | 8 +- .../assets/locale/locale.constant-en_US.json | 728 +++++++++++++++ 266 files changed, 15639 insertions(+), 83 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/assign-customer-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/assign-customer-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/attributes-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/attributes-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/clear-alarm-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/clear-alarm-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/create-alarm-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/create-alarm-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/create-relation-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/create-relation-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/delete-attributes-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/delete-attributes-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/delete-relation-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/delete-relation-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/device-profile-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/device-profile-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/device-state-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/device-state-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/generator-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/generator-config.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/generator-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/gps-geo-action-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/gps-geo-action-config.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/gps-geo-action-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/log-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/log-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/math-function-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/math-function-config.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/math-function-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/msg-count-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/msg-count-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/msg-delay-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/msg-delay-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/push-to-cloud-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/push-to-cloud-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/push-to-edge-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/push-to-edge-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/rpc-reply-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/rpc-reply-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/rpc-request-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/rpc-request-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/rule-node-config-action.module.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/save-to-custom-table-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/save-to-custom-table-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/send-rest-api-call-reply-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/send-rest-api-call-reply-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/unassign-customer-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/unassign-customer-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/alarm-status-select.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/alarm-status-select.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/alarm-status-select.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/arguments-map-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/arguments-map-config.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/arguments-map-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/credentials-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/credentials-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/device-relations-query-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/device-relations-query-config.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/device-relations-query-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/example-hint.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/example-hint.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/example-hint.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config-old.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config-old.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config-old.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/math-function-autocomplete.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/math-function-autocomplete.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/message-types-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/message-types-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/msg-metadata-chip.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/msg-metadata-chip.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/output-message-type-autocomplete.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/output-message-type-autocomplete.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config-old.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config-old.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/rule-node-config-common.module.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/select-attributes.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/select-attributes.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/sv-map-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/sv-map-config.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/sv-map-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/empty-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/enrichment/calculate-delta-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/enrichment/calculate-delta-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/enrichment/customer-attributes-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/enrichment/customer-attributes-config.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/enrichment/customer-attributes-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/enrichment/device-attributes-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/enrichment/device-attributes-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/enrichment/entity-details-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/enrichment/entity-details-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/enrichment/fetch-device-credentials-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/enrichment/fetch-device-credentials-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/enrichment/get-telemetry-from-database-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/enrichment/get-telemetry-from-database-config.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/enrichment/get-telemetry-from-database-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/enrichment/originator-attributes-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/enrichment/originator-attributes-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/enrichment/originator-fields-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/enrichment/originator-fields-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/enrichment/related-attributes-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/enrichment/related-attributes-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/enrichment/rule-node-core-enrichment.module.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/enrichment/tenant-attributes-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/enrichment/tenant-attributes-config.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/enrichment/tenant-attributes-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/azure-iot-hub-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/azure-iot-hub-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/kafka-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/kafka-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/lambda-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/lambda-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/mqtt-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/mqtt-config.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/mqtt-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/notification-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/notification-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/pubsub-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/pubsub-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/rabbit-mq-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/rabbit-mq-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/rest-api-call-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/rest-api-call-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/rule-node-config-external.module.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/send-email-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/send-email-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/send-sms-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/send-sms-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/slack-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/slack-config.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/slack-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/sns-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/sns-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/sqs-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/external/sqs-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/filter/check-alarm-status.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/filter/check-alarm-status.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/filter/check-message-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/filter/check-message-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/filter/check-relation-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/filter/check-relation-config.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/filter/check-relation-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/filter/gps-geo-filter-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/filter/gps-geo-filter-config.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/filter/gps-geo-filter-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/filter/message-type-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/filter/message-type-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/filter/originator-type-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/filter/originator-type-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/filter/rule-node-config-filter.module.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/filter/script-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/filter/script-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/filter/switch-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/filter/switch-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/flow/rule-chain-input.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/flow/rule-chain-input.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/flow/rule-chain-output.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/flow/rule-chain-output.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/flow/rule-node-config-flow.module.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.models.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.module.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/transform/change-originator-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/transform/change-originator-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/transform/copy-keys-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/transform/copy-keys-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/transform/deduplication-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/transform/deduplication-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/transform/delete-keys-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/transform/delete-keys-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/transform/node-json-path-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/transform/node-json-path-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/transform/rename-keys-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/transform/rename-keys-config.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/transform/rename-keys-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/transform/rule-node-config-transform.module.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/transform/script-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/transform/script-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/transform/to-email-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/transform/to-email-config.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/transform/to-email-config.component.ts diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNode.java index 01dd176896..d6afd971b4 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNode.java @@ -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 diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java index 622448f494..55ba8ae59a 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java @@ -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 msg property. For example 'temperature = ' + msg.temperature ;. " + "Message metadata can be accessed via metadata property. For example 'name = ' + metadata.customerName;.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeClearAlarmConfig", icon = "notifications_off" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java index a4b0227551..1a1bcf7daf 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java @@ -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" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java index 7e535596cd..c1e8ae89aa 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java @@ -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 msg property. For example 'temperature = ' + msg.temperature ;. " + "Message metadata can be accessed via metadata property. For example 'name = ' + metadata.customerName;.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeCreateAlarmConfig", icon = "notifications_active" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNode.java index 7571695b49..0ae90cd6a7 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNode.java @@ -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." + "

  • Change originator to target entity - useful when you need to process submitted message as a message from target entity.
  • " + "Output connections: Success - if the relation already exists or successfully created, otherwise Failure.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeCreateRelationConfig", icon = "add_circle", version = 1 diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNode.java index dbc914065f..abbaad7cb3 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNode.java @@ -53,7 +53,6 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; "
  • User - use a user with the specified email as the target entity to delete relation with.
  • " + "
  • Edge - use an edge with the specified name as the target entity to delete relation with.
  • " + "Output connections: Success - If the relation(s) successfully deleted, otherwise Failure.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeDeleteRelationConfig", icon = "remove_circle", version = 1 diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeviceStateNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeviceStateNode.java index 7925178b33..b00023e59d 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeviceStateNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeviceStateNode.java @@ -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 { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java index 6b07d41a42..cdbaca491e 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java @@ -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 msg property. For example 'temperature = ' + msg.temperature ;. " + "Message metadata can be accessed via metadata property. For example 'name = ' + metadata.customerName;.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeLogConfig", icon = "menu" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java index edbd9c7ca4..b387877f07 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java @@ -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 { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java index a0dbc5da97..5c485353ab 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java @@ -69,7 +69,6 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; "Note:If the mapping key is $entity_id, that is identified by the Message Originator, then to the appropriate column name(mapping value) will be write the message originator id.

    " + "If specified message field does not exist or is not a JSON Primitive, the outbound message will be routed via failure chain," + " otherwise, the message will be routed via success chain.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeCustomTableConfig", icon = "file_upload", ruleChainTypes = RuleChainType.CORE) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNode.java index 93fbe8019e..9569d0be77 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNode.java @@ -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.

    " + "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 diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNode.java index bb9f5416db..6cf01f80c7 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNode.java @@ -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.

    " + "Output connections: Success, Failure.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbExternalNodeLambdaConfig", iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4Ij48cGF0aCBkPSJNMTMuMjMgMTAuNTZWMTBjLTEuOTQgMC0zLjk5LjM5LTMuOTkgMi42NyAwIDEuMTYuNjEgMS45NSAxLjYzIDEuOTUuNzYgMCAxLjQzLS40NyAxLjg2LTEuMjIuNTItLjkzLjUtMS44LjUtMi44NG0yLjcgNi41M2MtLjE4LjE2LS40My4xNy0uNjMuMDYtLjg5LS43NC0xLjA1LTEuMDgtMS41NC0xLjc5LTEuNDcgMS41LTIuNTEgMS45NS00LjQyIDEuOTUtMi4yNSAwLTQuMDEtMS4zOS00LjAxLTQuMTcgMC0yLjE4IDEuMTctMy42NCAyLjg2LTQuMzggMS40Ni0uNjQgMy40OS0uNzYgNS4wNC0uOTNWNy41YzAtLjY2LjA1LTEuNDEtLjMzLTEuOTYtLjMyLS40OS0uOTUtLjctMS41LS43LTEuMDIgMC0xLjkzLjUzLTIuMTUgMS42MS0uMDUuMjQtLjI1LjQ4LS40Ny40OWwtMi42LS4yOGMtLjIyLS4wNS0uNDYtLjIyLS40LS41Ni42LTMuMTUgMy40NS00LjEgNi00LjEgMS4zIDAgMyAuMzUgNC4wMyAxLjMzQzE3LjExIDQuNTUgMTcgNi4xOCAxNyA3Ljk1djQuMTdjMCAxLjI1LjUgMS44MSAxIDIuNDguMTcuMjUuMjEuNTQgMCAuNzFsLTIuMDYgMS43OGgtLjAxIj48L3BhdGg+PHBhdGggZD0iTTIwLjE2IDE5LjU0QzE4IDIxLjE0IDE0LjgyIDIyIDEyLjEgMjJjLTMuODEgMC03LjI1LTEuNDEtOS44NS0zLjc2LS4yLS4xOC0uMDItLjQzLjI1LS4yOSAyLjc4IDEuNjMgNi4yNSAyLjYxIDkuODMgMi42MSAyLjQxIDAgNS4wNy0uNSA3LjUxLTEuNTMuMzctLjE2LjY2LjI0LjMyLjUxIj48L3BhdGg+PHBhdGggZD0iTTIxLjA3IDE4LjVjLS4yOC0uMzYtMS44NS0uMTctMi41Ny0uMDgtLjE5LjAyLS4yMi0uMTYtLjAzLS4zIDEuMjQtLjg4IDMuMjktLjYyIDMuNTMtLjMzLjI0LjMtLjA3IDIuMzUtMS4yNCAzLjMyLS4xOC4xNi0uMzUuMDctLjI2LS4xMS4yNi0uNjcuODUtMi4xNC41Ny0yLjV6Ij48L3BhdGg+PC9zdmc+" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sns/TbSnsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sns/TbSnsNode.java index 2c732150e1..3a9733ee82 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sns/TbSnsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sns/TbSnsNode.java @@ -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 " + "(messageId, requestId) in the Message Metadata from the AWS SNS. " + "For example requestId field can be accessed with metadata.requestId.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbExternalNodeSnsConfig", iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4Ij48cGF0aCBkPSJNMTMuMjMgMTAuNTZWMTBjLTEuOTQgMC0zLjk5LjM5LTMuOTkgMi42NyAwIDEuMTYuNjEgMS45NSAxLjYzIDEuOTUuNzYgMCAxLjQzLS40NyAxLjg2LTEuMjIuNTItLjkzLjUtMS44LjUtMi44NG0yLjcgNi41M2MtLjE4LjE2LS40My4xNy0uNjMuMDYtLjg5LS43NC0xLjA1LTEuMDgtMS41NC0xLjc5LTEuNDcgMS41LTIuNTEgMS45NS00LjQyIDEuOTUtMi4yNSAwLTQuMDEtMS4zOS00LjAxLTQuMTcgMC0yLjE4IDEuMTctMy42NCAyLjg2LTQuMzggMS40Ni0uNjQgMy40OS0uNzYgNS4wNC0uOTNWNy41YzAtLjY2LjA1LTEuNDEtLjMzLTEuOTYtLjMyLS40OS0uOTUtLjctMS41LS43LTEuMDIgMC0xLjkzLjUzLTIuMTUgMS42MS0uMDUuMjQtLjI1LjQ4LS40Ny40OWwtMi42LS4yOGMtLjIyLS4wNS0uNDYtLjIyLS40LS41Ni42LTMuMTUgMy40NS00LjEgNi00LjEgMS4zIDAgMyAuMzUgNC4wMyAxLjMzQzE3LjExIDQuNTUgMTcgNi4xOCAxNyA3Ljk1djQuMTdjMCAxLjI1LjUgMS44MSAxIDIuNDguMTcuMjUuMjEuNTQgMCAuNzFsLTIuMDYgMS43OGgtLjAxIj48L3BhdGg+PHBhdGggZD0iTTIwLjE2IDE5LjU0QzE4IDIxLjE0IDE0LjgyIDIyIDEyLjEgMjJjLTMuODEgMC03LjI1LTEuNDEtOS44NS0zLjc2LS4yLS4xOC0uMDItLjQzLjI1LS4yOSAyLjc4IDEuNjMgNi4yNSAyLjYxIDkuODMgMi42MSAyLjQxIDAgNS4wNy0uNSA3LjUxLTEuNTMuMzctLjE2LjY2LjI0LjMyLjUxIj48L3BhdGg+PHBhdGggZD0iTTIxLjA3IDE4LjVjLS4yOC0uMzYtMS44NS0uMTctMi41Ny0uMDgtLjE5LjAyLS4yMi0uMTYtLjAzLS4zIDEuMjQtLjg4IDMuMjktLjYyIDMuNTMtLjMzLjI0LjMtLjA3IDIuMzUtMS4yNCAzLjMyLS4xOC4xNi0uMzUuMDctLjI2LS4xMS4yNi0uNjcuODUtMi4xNC41Ny0yLjV6Ij48L3BhdGg+PC9zdmc+" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNode.java index e61cd537c4..d9b6120fc7 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNode.java @@ -52,7 +52,6 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; "response fields (messageId, requestId, messageBodyMd5, messageAttributesMd5" + ", sequenceNumber) in the Message Metadata from the AWS SQS." + " For example requestId field can be accessed with metadata.requestId.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbExternalNodeSqsConfig", iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4Ij48cGF0aCBkPSJNMTMuMjMgMTAuNTZWMTBjLTEuOTQgMC0zLjk5LjM5LTMuOTkgMi42NyAwIDEuMTYuNjEgMS45NSAxLjYzIDEuOTUuNzYgMCAxLjQzLS40NyAxLjg2LTEuMjIuNTItLjkzLjUtMS44LjUtMi44NG0yLjcgNi41M2MtLjE4LjE2LS40My4xNy0uNjMuMDYtLjg5LS43NC0xLjA1LTEuMDgtMS41NC0xLjc5LTEuNDcgMS41LTIuNTEgMS45NS00LjQyIDEuOTUtMi4yNSAwLTQuMDEtMS4zOS00LjAxLTQuMTcgMC0yLjE4IDEuMTctMy42NCAyLjg2LTQuMzggMS40Ni0uNjQgMy40OS0uNzYgNS4wNC0uOTNWNy41YzAtLjY2LjA1LTEuNDEtLjMzLTEuOTYtLjMyLS40OS0uOTUtLjctMS41LS43LTEuMDIgMC0xLjkzLjUzLTIuMTUgMS42MS0uMDUuMjQtLjI1LjQ4LS40Ny40OWwtMi42LS4yOGMtLjIyLS4wNS0uNDYtLjIyLS40LS41Ni42LTMuMTUgMy40NS00LjEgNi00LjEgMS4zIDAgMyAuMzUgNC4wMyAxLjMzQzE3LjExIDQuNTUgMTcgNi4xOCAxNyA3Ljk1djQuMTdjMCAxLjI1LjUgMS44MSAxIDIuNDguMTcuMjUuMjEuNTQgMCAuNzFsLTIuMDYgMS43OGgtLjAxIj48L3BhdGg+PHBhdGggZD0iTTIwLjE2IDE5LjU0QzE4IDIxLjE0IDE0LjgyIDIyIDEyLjEgMjJjLTMuODEgMC03LjI1LTEuNDEtOS44NS0zLjc2LS4yLS4xOC0uMDItLjQzLjI1LS4yOSAyLjc4IDEuNjMgNi4yNSAyLjYxIDkuODMgMi42MSAyLjQxIDAgNS4wNy0uNSA3LjUxLTEuNTMuMzctLjE2LjY2LjI0LjMyLjUxIj48L3BhdGg+PHBhdGggZD0iTTIxLjA3IDE4LjVjLS4yOC0uMzYtMS44NS0uMTctMi41Ny0uMDgtLjE5LjAyLS4yMi0uMTYtLjAzLS4zIDEuMjQtLjg4IDMuMjktLjYyIDMuNTMtLjMzLjI0LjMtLjA3IDIuMzUtMS4yNCAzLjMyLS4xOC4xNi0uMzUuMDctLjI2LS4xMS4yNi0uNjcuODUtMi4xNC41Ny0yLjV6Ij48L3BhdGg+PC9zdmc+" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java index c7bf1423f8..0fcabd806a 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java @@ -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" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java index cf4b5b4ce1..891efec861 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java @@ -59,7 +59,6 @@ import static org.thingsboard.server.common.data.DataConstants.QUEUE_NAME; "
  • ALL - return all messages as a single JSON array message. " + "Where each element represents object with msg and metadata inner properties.
  • ", icon = "content_copy", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeMsgDeduplicationConfig" ) @Slf4j diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java index a8a2c4e9b5..eb18bd13fe 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java @@ -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 { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/TbMsgPushToCloudNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/TbMsgPushToCloudNode.java index 1ed2f8e849..50355632a3 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/TbMsgPushToCloudNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/TbMsgPushToCloudNode.java @@ -46,7 +46,6 @@ import java.util.UUID; "
    ALARM

    " + "Message will be routed via Failure 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 Success route.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodePushToCloudConfig", icon = "cloud_upload", ruleChainTypes = RuleChainType.EDGE diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNode.java index 4ae86742a2..2659bb281e 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNode.java @@ -61,7 +61,6 @@ import static org.thingsboard.server.dao.edge.BaseRelatedEdgesService.RELATED_ED "
    ALARM

    " + "Message will be routed via Failure 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 Success route.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodePushToEdgeConfig", icon = "cloud_download", ruleChainTypes = RuleChainType.CORE diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNode.java index 0fc03be83b..b0f5552b72 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNode.java @@ -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.

    " + "Output connections: Asset profile name or Failure", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbNodeEmptyConfig") public class TbAssetTypeSwitchNode extends TbAbstractTypeSwitchNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNode.java index ed703e8661..6aa05007d1 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNode.java @@ -43,7 +43,6 @@ import java.util.Objects; nodeDescription = "Checks alarm status.", nodeDetails = "Checks the alarm status to match one of the specified statuses.

    " + "Output connections: True, False, Failure.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeCheckAlarmStatusConfig") public class TbCheckAlarmStatusNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckMessageNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckMessageNode.java index 1b9f2f2a78..0ca3f74d29 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckMessageNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckMessageNode.java @@ -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.

    " + "Output connections: True, False, Failure", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeCheckMessageConfig") public class TbCheckMessageNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNode.java index 0356548a03..f24312dba9 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNode.java @@ -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.

    " + "Output connections: True, False, Failure", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeCheckRelationConfig") public class TbCheckRelationNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbDeviceTypeSwitchNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbDeviceTypeSwitchNode.java index d440969fff..a7ec1e86a4 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbDeviceTypeSwitchNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbDeviceTypeSwitchNode.java @@ -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

    " + "Output connections: Device profile name or Failure", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbNodeEmptyConfig") public class TbDeviceTypeSwitchNode extends TbAbstractTypeSwitchNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java index 6d5aaa6477..767c2f91d6 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java @@ -44,7 +44,6 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; "Message metadata can be accessed via metadata property. For example metadata.customerName === 'John';
    " + "Message type can be accessed via msgType property.

    " + "Output connections: True, False, Failure", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeScriptConfig" ) public class TbJsFilterNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java index e866f05bd0..10277d067b 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java @@ -46,7 +46,6 @@ import java.util.Set; "Message metadata can be accessed via metadata property. For example metadata.customerName === 'John';
    " + "Message type can be accessed via msgType property.

    " + "Output connections: Custom connection(s) defined by switch node or Failure", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeSwitchConfig") public class TbJsSwitchNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNode.java index 579b6a3ec7..d818c3a8f1 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNode.java @@ -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 True chain, otherwise False chain is used.

    " + "Output connections: True, False, Failure", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeMessageTypeConfig") public class TbMsgTypeFilterNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java index 971ab22588..455bf2f78c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java @@ -36,7 +36,6 @@ import org.thingsboard.server.common.msg.TbMsg; nodeDetails = "Sends messages with message types \"Post attributes\", \"Post telemetry\", \"RPC Request\"" + " etc. via corresponding chain, otherwise Other chain is used.

    " + "Output connections: Message type connection, Other - if message type is custom or Failure", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbNodeEmptyConfig") public class TbMsgTypeSwitchNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNode.java index 94a13be583..ad873666c0 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNode.java @@ -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.

    " + "Output connections: True, False, Failure", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeOriginatorTypeConfig") public class TbOriginatorTypeFilterNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNode.java index 2eef991aa0..95f062e88f 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNode.java @@ -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.).

    " + "Output connections: Message originator type or Failure", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbNodeEmptyConfig") public class TbOriginatorTypeSwitchNode extends TbAbstractTypeSwitchNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbAckNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbAckNode.java index 505a5b3215..b19488f0ca 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbAckNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbAckNode.java @@ -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 { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNode.java index cd827c9e54..8b9f113621 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNode.java @@ -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 { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbRuleChainInputNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbRuleChainInputNode.java index 0b30d02842..a594f548e8 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbRuleChainInputNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbRuleChainInputNode.java @@ -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.

    " + "Output connections: Any connection(s) produced by output node(s) in the target rule chain.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFlowNodeRuleChainInputConfig", relationTypes = {}, ruleChainNode = true, diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbRuleChainOutputNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbRuleChainOutputNode.java index 2879397ace..1d887aa3a0 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbRuleChainOutputNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbRuleChainOutputNode.java @@ -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 ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java index d6d3bb3c11..45fe4f8bc0 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java @@ -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 " + "(messageId in the Message Metadata from the GCP PubSub. " + "messageId field can be accessed with metadata.messageId.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbExternalNodePubSubConfig", iconUrl = "data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiB2aWV3Qm94PSIwIDAgMTI4IDEyOCI+Cjx0aXRsZT5DbG91ZCBQdWJTdWI8L3RpdGxlPgo8Zz4KPHBhdGggZD0iTTEyNi40Nyw1OC4xMmwtMjYuMy00NS43NEExMS41NiwxMS41NiwwLDAsMCw5MC4zMSw2LjVIMzcuN2ExMS41NSwxMS41NSwwLDAsMC05Ljg2LDUuODhMMS41Myw1OGExMS40OCwxMS40OCwwLDAsMCwwLDExLjQ0bDI2LjMsNDZhMTEuNzcsMTEuNzcsMCwwLDAsOS44Niw2LjA5SDkwLjNhMTEuNzMsMTEuNzMsMCwwLDAsOS44Ny02LjA2bDI2LjMtNDUuNzRBMTEuNzMsMTEuNzMsMCwwLDAsMTI2LjQ3LDU4LjEyWiIgc3R5bGU9ImZpbGw6ICM3MzViMmYiLz4KPHBhdGggZD0iTTg5LjIyLDQ3Ljc0LDgzLjM2LDQ5bC0xNC42LTE0LjZMNjQuMDksNDMuMSw2MS41NSw1My4ybDQuMjksNC4yOUw1Ny42LDU5LjE4LDQ2LjMsNDcuODhsLTcuNjcsNy4zOEw1Mi43Niw2OS4zN2wtMTUsMTEuOUw3OCwxMjEuNUg5MC4zYTExLjczLDExLjczLDAsMCwwLDkuODctNi4wNmwyMC43Mi0zNloiIHN0eWxlPSJvcGFjaXR5OiAwLjA3MDAwMDAwMDI5ODAyMztpc29sYXRpb246IGlzb2xhdGUiLz4KPHBhdGggZD0iTTgyLjg2LDQ3YTUuMzIsNS4zMiwwLDEsMS0xLjk1LDcuMjdBNS4zMiw1LjMyLDAsMCwxLDgyLjg2LDQ3IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNMzkuODIsNTYuMThhNS4zMiw1LjMyLDAsMSwxLDcuMjctMS45NSw1LjMyLDUuMzIsMCwwLDEtNy4yNywxLjk1IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNNjkuMzIsODguODVBNS4zMiw1LjMyLDAsMSwxLDY0LDgzLjUyYTUuMzIsNS4zMiwwLDAsMSw1LjMyLDUuMzIiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxnPgo8cGF0aCBkPSJNNjQsNTIuOTRhMTEuMDYsMTEuMDYsMCwwLDEsMi40Ni4yOFYzOS4xNUg2MS41NFY1My4yMkExMS4wNiwxMS4wNiwwLDAsMSw2NCw1Mi45NFoiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxwYXRoIGQ9Ik03NC41Nyw2Ny4yNmExMSwxMSwwLDAsMS0yLjQ3LDQuMjVsMTIuMTksNywyLjQ2LTQuMjZaIiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNNTMuNDMsNjcuMjZsLTEyLjE4LDcsMi40Niw0LjI2LDEyLjE5LTdBMTEsMTEsMCwwLDEsNTMuNDMsNjcuMjZaIiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8L2c+CjxwYXRoIGQ9Ik03Mi42LDY0QTguNiw4LjYsMCwxLDEsNjQsNTUuNCw4LjYsOC42LDAsMCwxLDcyLjYsNjQiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxwYXRoIGQ9Ik0zOS4xLDcwLjU3YTYuNzYsNi43NiwwLDEsMS0yLjQ3LDkuMjMsNi43Niw2Ljc2LDAsMCwxLDIuNDctOS4yMyIgc3R5bGU9ImZpbGw6ICNmZmYiLz4KPHBhdGggZD0iTTgyLjE0LDgyLjI3YTYuNzYsNi43NiwwLDEsMSw5LjIzLTIuNDcsNi43NSw2Ljc1LDAsMCwxLTkuMjMsMi40NyIgc3R5bGU9ImZpbGw6ICNmZmYiLz4KPHBhdGggZD0iTTcwLjc2LDM5LjE1QTYuNzYsNi43NiwwLDEsMSw2NCwzMi4zOWE2Ljc2LDYuNzYsMCwwLDEsNi43Niw2Ljc2IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8L2c+Cjwvc3ZnPgo=" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java index b2d16434d5..a6cbb58cf5 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java @@ -66,7 +66,6 @@ import static org.thingsboard.rule.engine.util.GpsGeofencingEvents.OUTSIDE; "If the presence monitoring strategy \"On each message\" is selected, sends messages via rule node connection type Inside or Outside every time the geofencing condition is satisfied. " + "

    " + "Output connections: Entered, Left, Inside, Outside, Success", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeGpsGeofencingConfig" ) public class TbGpsGeofencingActionNode extends AbstractGeofencingNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java index 89ca1318b7..aa95f8e523 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java @@ -60,7 +60,6 @@ import org.thingsboard.server.common.msg.TbMsg; "

    " + "Available radius units: METER, KILOMETER, FOOT, MILE, NAUTICAL_MILE;

    " + "Output connections: True, False, Failure", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeGpsGeofencingConfig") public class TbGpsGeofencingFilterNode extends AbstractGeofencingNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java index 865eac0aac..5a9e917427 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java @@ -52,7 +52,6 @@ import java.util.Properties; nodeDetails = "Will send record via Kafka producer to Kafka server. " + "Outbound message will contain response fields (offset, partition, topic)" + " from the Kafka in the Message Metadata. For example partition field can be accessed with metadata.partition.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbExternalNodeKafkaConfig", iconUrl = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTUzOCIgaGVpZ2h0PSIyNTAwIiB2aWV3Qm94PSIwIDAgMjU2IDQxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+PHBhdGggZD0iTTIwMS44MTYgMjMwLjIxNmMtMTYuMTg2IDAtMzAuNjk3IDcuMTcxLTQwLjYzNCAxOC40NjFsLTI1LjQ2My0xOC4wMjZjMi43MDMtNy40NDIgNC4yNTUtMTUuNDMzIDQuMjU1LTIzLjc5NyAwLTguMjE5LTEuNDk4LTE2LjA3Ni00LjExMi0yMy40MDhsMjUuNDA2LTE3LjgzNWM5LjkzNiAxMS4yMzMgMjQuNDA5IDE4LjM2NSA0MC41NDggMTguMzY1IDI5Ljg3NSAwIDU0LjE4NC0yNC4zMDUgNTQuMTg0LTU0LjE4NCAwLTI5Ljg3OS0yNC4zMDktNTQuMTg0LTU0LjE4NC01NC4xODQtMjkuODc1IDAtNTQuMTg0IDI0LjMwNS01NC4xODQgNTQuMTg0IDAgNS4zNDguODA4IDEwLjUwNSAyLjI1OCAxNS4zODlsLTI1LjQyMyAxNy44NDRjLTEwLjYyLTEzLjE3NS0yNS45MTEtMjIuMzc0LTQzLjMzMy0yNS4xODJ2LTMwLjY0YzI0LjU0NC01LjE1NSA0My4wMzctMjYuOTYyIDQzLjAzNy01My4wMTlDMTI0LjE3MSAyNC4zMDUgOTkuODYyIDAgNjkuOTg3IDAgNDAuMTEyIDAgMTUuODAzIDI0LjMwNSAxNS44MDMgNTQuMTg0YzAgMjUuNzA4IDE4LjAxNCA0Ny4yNDYgNDIuMDY3IDUyLjc2OXYzMS4wMzhDMjUuMDQ0IDE0My43NTMgMCAxNzIuNDAxIDAgMjA2Ljg1NGMwIDM0LjYyMSAyNS4yOTIgNjMuMzc0IDU4LjM1NSA2OC45NHYzMi43NzRjLTI0LjI5OSA1LjM0MS00Mi41NTIgMjcuMDExLTQyLjU1MiA1Mi44OTQgMCAyOS44NzkgMjQuMzA5IDU0LjE4NCA1NC4xODQgNTQuMTg0IDI5Ljg3NSAwIDU0LjE4NC0yNC4zMDUgNTQuMTg0LTU0LjE4NCAwLTI1Ljg4My0xOC4yNTMtNDcuNTUzLTQyLjU1Mi01Mi44OTR2LTMyLjc3NWE2OS45NjUgNjkuOTY1IDAgMCAwIDQyLjYtMjQuNzc2bDI1LjYzMyAxOC4xNDNjLTEuNDIzIDQuODQtMi4yMiA5Ljk0Ni0yLjIyIDE1LjI0IDAgMjkuODc5IDI0LjMwOSA1NC4xODQgNTQuMTg0IDU0LjE4NCAyOS44NzUgMCA1NC4xODQtMjQuMzA1IDU0LjE4NC01NC4xODQgMC0yOS44NzktMjQuMzA5LTU0LjE4NC01NC4xODQtNTQuMTg0em0wLTEyNi42OTVjMTQuNDg3IDAgMjYuMjcgMTEuNzg4IDI2LjI3IDI2LjI3MXMtMTEuNzgzIDI2LjI3LTI2LjI3IDI2LjI3LTI2LjI3LTExLjc4Ny0yNi4yNy0yNi4yN2MwLTE0LjQ4MyAxMS43ODMtMjYuMjcxIDI2LjI3LTI2LjI3MXptLTE1OC4xLTQ5LjMzN2MwLTE0LjQ4MyAxMS43ODQtMjYuMjcgMjYuMjcxLTI2LjI3czI2LjI3IDExLjc4NyAyNi4yNyAyNi4yN2MwIDE0LjQ4My0xMS43ODMgMjYuMjctMjYuMjcgMjYuMjdzLTI2LjI3MS0xMS43ODctMjYuMjcxLTI2LjI3em01Mi41NDEgMzA3LjI3OGMwIDE0LjQ4My0xMS43ODMgMjYuMjctMjYuMjcgMjYuMjdzLTI2LjI3MS0xMS43ODctMjYuMjcxLTI2LjI3YzAtMTQuNDgzIDExLjc4NC0yNi4yNyAyNi4yNzEtMjYuMjdzMjYuMjcgMTEuNzg3IDI2LjI3IDI2LjI3em0tMjYuMjcyLTExNy45N2MtMjAuMjA1IDAtMzYuNjQyLTE2LjQzNC0zNi42NDItMzYuNjM4IDAtMjAuMjA1IDE2LjQzNy0zNi42NDIgMzYuNjQyLTM2LjY0MiAyMC4yMDQgMCAzNi42NDEgMTYuNDM3IDM2LjY0MSAzNi42NDIgMCAyMC4yMDQtMTYuNDM3IDM2LjYzOC0zNi42NDEgMzYuNjM4em0xMzEuODMxIDY3LjE3OWMtMTQuNDg3IDAtMjYuMjctMTEuNzg4LTI2LjI3LTI2LjI3MXMxMS43ODMtMjYuMjcgMjYuMjctMjYuMjcgMjYuMjcgMTEuNzg3IDI2LjI3IDI2LjI3YzAgMTQuNDgzLTExLjc4MyAyNi4yNzEtMjYuMjcgMjYuMjcxeiIvPjwvc3ZnPg==" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNode.java index 864528b3a4..dd26a64993 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNode.java @@ -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 SEND_EMAIL.

    " + "Output connections: Success, Failure.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbTransformationNodeToEmailConfig", icon = "email" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java index 7be40fc829..27fdce68d9 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java @@ -44,7 +44,6 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; nodeDetails = "Expects messages with SEND_EMAIL type. Node works only with messages that " + " where created using to Email transformation Node, please connect this Node " + "with to Email Node using Successful chain.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbExternalNodeSendEmailConfig", icon = "send" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java index d72a23708e..27afa06f90 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java @@ -76,7 +76,6 @@ import static org.thingsboard.rule.engine.math.TbMathArgumentType.CONSTANT; "

    " + "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" diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNode.java index 75d0e774a2..48c3ba4778 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNode.java @@ -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.

    " + "Output connections: Success, Other or Failure.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeCalculateDeltaConfig") public class CalculateDeltaNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNode.java index 3362d60fac..6d11419e72 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNode.java @@ -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.

    " + "Output connections: Success, Failure.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeFetchDeviceCredentialsConfig") public class TbFetchDeviceCredentialsNode extends TbAbstractNodeWithFetchTo { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java index f618befb97..586b5077e2 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java @@ -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.

    " + "Output connections: Success, Failure.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeOriginatorAttributesConfig") public class TbGetAttributesNode extends TbAbstractGetAttributesNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java index ea48f3a2c4..37a9d052e6 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java @@ -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.

    " + "Output connections: Success, Failure.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeCustomerAttributesConfig") public class TbGetCustomerAttributeNode extends TbAbstractGetEntityDataNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNode.java index eb6eeb1e06..33cc5812e9 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNode.java @@ -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.

    " + "Output connections: Success, Failure.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeEntityDetailsConfig") public class TbGetCustomerDetailsNode extends TbAbstractGetEntityDetailsNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNode.java index 0e257ac3be..1dac58224c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNode.java @@ -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.

    " + "Output connections: Success, Failure.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeDeviceAttributesConfig") public class TbGetDeviceAttrNode extends TbAbstractGetAttributesNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNode.java index 391289cc5d..3807097349 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNode.java @@ -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.

    " + "Output connections: Success, Failure.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeOriginatorFieldsConfig") public class TbGetOriginatorFieldsNode extends TbAbstractGetMappedDataNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java index d107de25c6..639f234f7e 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java @@ -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.

    " + "Output connections: Success, Failure.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeRelatedAttributesConfig") public class TbGetRelatedAttributeNode extends TbAbstractGetEntityDataNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNode.java index 8e6e64bb17..b5d9361d50 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNode.java @@ -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.

    " + "Output connections: Success, Failure.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeGetTelemetryFromDatabase") public class TbGetTelemetryNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java index c1f8e95896..5738eb801b 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java @@ -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.

    " + "Output connections: Success, Failure.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeTenantAttributesConfig") public class TbGetTenantAttributeNode extends TbAbstractGetEntityDataNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNode.java index c18cb1f942..ef54aae34e 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNode.java @@ -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.

    " + "Output connections: Success, Failure.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeEntityDetailsConfig") public class TbGetTenantDetailsNode extends TbAbstractGetEntityDetailsNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java index 56bfe038a8..9576384ab4 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java @@ -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 AT_LEAST_ONCE.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbExternalNodeMqttConfig", icon = "call_split" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/azure/TbAzureIotHubNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/azure/TbAzureIotHubNode.java index cbf431d63a..836fb215d9 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/azure/TbAzureIotHubNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/azure/TbAzureIotHubNode.java @@ -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 AT_LEAST_ONCE.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbExternalNodeAzureIotHubConfig" ) public class TbAzureIotHubNode extends TbMqttNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java index 8ae3da7bad..4777a411dc 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java @@ -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" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNode.java index 78e0b1e46c..a9c62c8d80 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNode.java @@ -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+" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java index c57a9a1433..9df674cf88 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java @@ -59,7 +59,6 @@ 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" ) public class TbDeviceProfileNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNode.java index 8b724ae86e..ebd2a57e59 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNode.java @@ -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=" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java index 0e65905dfe..d25e1d647c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java @@ -45,7 +45,6 @@ import java.util.List; "For example statusCode field can be accessed with metadata.statusCode." + "
    Note- 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+" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbSendRestApiCallReplyNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbSendRestApiCallReplyNode.java index 84441fb213..0c6e449576 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbSendRestApiCallReplyNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbSendRestApiCallReplyNode.java @@ -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" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNode.java index c343b242bf..1f071b590e 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNode.java @@ -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" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java index 81863f9410..76d6649734 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java @@ -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" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/sms/TbSendSmsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/sms/TbSendSmsNode.java index 0024936b89..3fbd074a14 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/sms/TbSendSmsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/sms/TbSendSmsNode.java @@ -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" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java index e83a125265..c57c62e2ae 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java @@ -62,7 +62,6 @@ import static org.thingsboard.server.common.data.msg.TbMsgType.POST_ATTRIBUTES_R "Additionally if checkbox Send attributes updated notification is set to true, rule node will put the \"Attributes Updated\" " + "event for SHARED_SCOPE and SERVER_SCOPE 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" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNode.java index f1f1a29f3c..a89a4f37d8 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNode.java @@ -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 Success chain, otherwise, Failure chain is used.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeDeleteAttributesConfig", icon = "remove_circle" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java index 27f45feb47..b0bdb1de69 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java @@ -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" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transaction/TbSynchronizationBeginNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transaction/TbSynchronizationBeginNode.java index 543767cfeb..50d668b1d4 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transaction/TbSynchronizationBeginNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transaction/TbSynchronizationBeginNode.java @@ -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 { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transaction/TbSynchronizationEndNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transaction/TbSynchronizationEndNode.java index 6f1344acf3..9407414dcb 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transaction/TbSynchronizationEndNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transaction/TbSynchronizationEndNode.java @@ -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 diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java index d590bbbb75..e45f1dd8b2 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java @@ -54,7 +54,6 @@ import static org.thingsboard.rule.engine.transform.OriginatorSource.RELATED; "
  • Entity by name pattern - specify entity type and name pattern of new originator. Following entity types are supported: " + "'Device', 'Asset', 'Entity View', 'Edge' or 'User'.
  • " + "Output connections: Success, Failure.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbTransformationNodeChangeOriginatorConfig", icon = "find_replace" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNode.java index 1417618a54..aadc3a3851 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNode.java @@ -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.

    " + "Output connections: Success, Failure.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbTransformationNodeCopyKeysConfig", icon = "content_copy" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNode.java index cf31381af2..412b523786 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNode.java @@ -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.

    " + "Output connections: Success, Failure.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbTransformationNodeDeleteKeysConfig", icon = "remove_circle" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbJsonPathNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbJsonPathNode.java index 043e914c2c..af9121e8a1 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbJsonPathNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbJsonPathNode.java @@ -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.

    " + "Output connections: Success, Failure.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, icon = "functions", configDirective = "tbTransformationNodeJsonPathConfig" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNode.java index 490504c42b..904e624c75 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNode.java @@ -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.

    " + "Output connections: Success, Failure.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbTransformationNodeRenameKeysConfig", icon = "find_replace" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNode.java index 92a3dd8d09..588d073cac 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNode.java @@ -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.

    " + "Output connections: Success, Failure.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, icon = "content_copy", configDirective = "tbNodeEmptyConfig" ) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNode.java index 3227c0c52c..01c6bd36c2 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNode.java @@ -41,7 +41,6 @@ import java.util.List; "{ msg: new payload,
       metadata: new metadata,
       msgType: new msgType }

    " + "All fields in resulting object are optional and will be taken from original message if not specified.

    " + "Output connections: Success, Failure.", - uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbTransformationNodeScriptConfig" ) public class TbTransformMsgNode extends TbAbstractTransformNode { diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/assign-customer-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/assign-customer-config.component.html new file mode 100644 index 0000000000..486764f936 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/assign-customer-config.component.html @@ -0,0 +1,35 @@ + +
    +
    + + tb.rulenode.customer-name-pattern + + + {{ 'tb.rulenode.customer-name-pattern-required' | translate }} + + tb.rulenode.customer-name-pattern-hint + +
    + + {{ 'tb.rulenode.create-customer-if-not-exists' | translate }} + +
    +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/assign-customer-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/assign-customer-config.component.ts new file mode 100644 index 0000000000..924cb448e3 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/assign-customer-config.component.ts @@ -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; + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/attributes-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/attributes-config.component.html new file mode 100644 index 0000000000..80633ca0d3 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/attributes-config.component.html @@ -0,0 +1,79 @@ + +
    +
    + + +
    + + {{ 'tb.rulenode.attributes-scope' | translate }} + + + {{ telemetryTypeTranslationsMap.get(scope) | translate }} + + + + + {{ 'tb.rulenode.attributes-scope-value' | translate }} + + + +
    +
    + +
    + + + tb.rulenode.advanced-settings + +
    + + {{ 'tb.rulenode.update-attributes-only-on-value-change' | translate }} + +
    +
    + + {{ 'tb.rulenode.send-attributes-updated-notification' | translate }} + +
    +
    + + {{ 'tb.rulenode.notify-device' | translate }} + +
    +
    +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/attributes-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/attributes-config.component.ts new file mode 100644 index 0000000000..a60a4ae40e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/attributes-config.component.ts @@ -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. +/// + +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'; + +@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.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}); + }); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/clear-alarm-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/clear-alarm-config.component.html new file mode 100644 index 0000000000..1a348734a8 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/clear-alarm-config.component.html @@ -0,0 +1,67 @@ + +
    + + + + + + + +
    + +
    + + tb.rulenode.alarm-type + + + {{ 'tb.rulenode.alarm-type-required' | translate }} + + tb.rulenode.general-pattern-hint + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/clear-alarm-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/clear-alarm-config.component.ts new file mode 100644 index 0000000000..f132d737eb --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/clear-alarm-config.component.ts @@ -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 = new EventEmitter(); + + readonly hasScript = true; + + readonly testScriptLabel = 'tb.rulenode.test-details-function'; + + constructor(protected store: Store, + 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('tb.rulenode.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(); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/create-alarm-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/create-alarm-config.component.html new file mode 100644 index 0000000000..9b1efd0f1a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/create-alarm-config.component.html @@ -0,0 +1,128 @@ + +
    + + {{ 'tb.rulenode.use-message-alarm-data' | translate }} + + + {{ 'tb.rulenode.overwrite-alarm-details' | translate }} + +
    + + + + + + + +
    + +
    +
    +
    + + tb.rulenode.alarm-type + + + {{ 'tb.rulenode.alarm-type-required' | translate }} + + tb.rulenode.general-pattern-hint + + + {{ 'tb.rulenode.use-alarm-severity-pattern' | translate }} + + + tb.rulenode.alarm-severity + + + {{ alarmSeverityTranslationMap.get(severity) | translate }} + + + + {{ 'tb.rulenode.alarm-severity-required' | translate }} + + + + tb.rulenode.alarm-severity-pattern + + + {{ 'tb.rulenode.alarm-severity-required' | translate }} + + + + + {{ 'tb.rulenode.propagate' | translate }} + +
    + + tb.rulenode.relation-types-list + + + {{key}} + close + + + + tb.rulenode.relation-types-list-hint + +
    + + {{ 'tb.rulenode.propagate-to-owner' | translate }} + + + {{ 'tb.rulenode.propagate-to-tenant' | translate }} + +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/create-alarm-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/create-alarm-config.component.ts new file mode 100644 index 0000000000..b5639f15ac --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/create-alarm-config.component.ts @@ -0,0 +1,199 @@ +/// +/// 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'; + +@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 = new EventEmitter(); + + readonly hasScript = true; + + readonly testScriptLabel = 'tb.rulenode.test-details-function'; + + constructor(protected store: Store, + 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.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('tb.rulenode.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(); + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/create-relation-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/create-relation-config.component.html new file mode 100644 index 0000000000..abbdb99f53 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/create-relation-config.component.html @@ -0,0 +1,100 @@ + +
    +
    +
    tb.rulenode.relation-parameters
    +
    + + relation.direction + + + {{ directionTypeTranslations.get(type) | translate }} + + + + + +
    +
    + +
    +
    tb.rulenode.target-entity
    +
    + + + + + {{ entityTypeNamePatternTranslation.get(createRelationConfigForm.get('entityType').value) | translate }} + + + + + tb.rulenode.profile-name + + +
    + + + +
    + + {{ 'tb.rulenode.create-entity-if-not-exists' | translate }} + +
    +
    +
    + + + tb.rulenode.advanced-settings + +
    +
    + + {{ 'tb.rulenode.remove-current-relations' | translate }} + +
    +
    + + {{ 'tb.rulenode.change-originator-to-related-entity' | translate }} + +
    +
    +
    +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/create-relation-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/create-relation-config.component.ts new file mode 100644 index 0000000000..53c8da3bd6 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/create-relation-config.component.ts @@ -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.FROM, 'tb.rulenode.search-direction-from'], + [EntitySearchDirection.TO, 'tb.rulenode.search-direction-to'], + ] + ); + + entityType = EntityType; + + entityTypeNamePatternTranslation = new Map( + [ + [EntityType.DEVICE, 'tb.rulenode.device-name-pattern'], + [EntityType.ASSET, 'tb.rulenode.asset-name-pattern'], + [EntityType.ENTITY_VIEW, 'tb.rulenode.entity-view-name-pattern'], + [EntityType.CUSTOMER, 'tb.rulenode.customer-title-pattern'], + [EntityType.USER, 'tb.rulenode.user-name-pattern'], + [EntityType.DASHBOARD, 'tb.rulenode.dashboard-name-pattern'], + [EntityType.EDGE, 'tb.rulenode.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; + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/delete-attributes-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/delete-attributes-config.component.html new file mode 100644 index 0000000000..7050cae88a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/delete-attributes-config.component.html @@ -0,0 +1,89 @@ + +
    +
    + + +
    + + {{ 'tb.rulenode.attributes-scope' | translate }} + + + {{ telemetryTypeTranslationsMap.get(scope) | translate }} + + + + + {{ 'tb.rulenode.attributes-scope-value' | translate }} + + + +
    +
    + + + {{ 'tb.rulenode.attributes-keys' | translate }} + + + {{key}} + close + + + + {{ 'tb.rulenode.attributes-keys-required' | translate }} + tb.rulenode.general-pattern-hint + + +
    + + + tb.rulenode.advanced-settings + +
    + + {{ 'tb.rulenode.send-attributes-deleted-notification' | translate }} + +
    +
    + + {{ 'tb.rulenode.notify-device' | translate }} + +
    +
    +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/delete-attributes-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/delete-attributes-config.component.ts new file mode 100644 index 0000000000..758e0ecb48 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/delete-attributes-config.component.ts @@ -0,0 +1,88 @@ +/// +/// 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'; + +@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.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 = ''; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/delete-relation-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/delete-relation-config.component.html new file mode 100644 index 0000000000..bfd091dc57 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/delete-relation-config.component.html @@ -0,0 +1,62 @@ + +
    +
    +
    tb.rulenode.relation-parameters
    +
    + + relation.direction + + + {{ directionTypeTranslations.get(type) | translate }} + + + + + +
    +
    +
    +
    + + {{ 'tb.rulenode.delete-relation-with-specific-entity' | translate }} + +
    +
    +
    + + + + {{ entityTypeNamePatternTranslation.get(deleteRelationConfigForm.get('entityType').value) | translate }} + + +
    + +
    +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/delete-relation-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/delete-relation-config.component.ts new file mode 100644 index 0000000000..a59793f767 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/delete-relation-config.component.ts @@ -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.FROM, 'tb.rulenode.del-relation-direction-from'], + [EntitySearchDirection.TO, 'tb.rulenode.del-relation-direction-to'], + ] + ); + + entityTypeNamePatternTranslation = new Map( + [ + [EntityType.DEVICE, 'tb.rulenode.device-name-pattern'], + [EntityType.ASSET, 'tb.rulenode.asset-name-pattern'], + [EntityType.ENTITY_VIEW, 'tb.rulenode.entity-view-name-pattern'], + [EntityType.CUSTOMER, 'tb.rulenode.customer-title-pattern'], + [EntityType.USER, 'tb.rulenode.user-name-pattern'], + [EntityType.DASHBOARD, 'tb.rulenode.dashboard-name-pattern'], + [EntityType.EDGE, 'tb.rulenode.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; + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/device-profile-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/device-profile-config.component.html new file mode 100644 index 0000000000..bb897c6a2d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/device-profile-config.component.html @@ -0,0 +1,32 @@ + +
    +
    tb.rulenode.device-profile-node-hint
    +
    + + {{ 'tb.rulenode.persist-alarm-rules' | translate }} + +
    +
    + + {{ 'tb.rulenode.fetch-alarm-rules' | translate }} + +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/device-profile-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/device-profile-config.component.ts new file mode 100644 index 0000000000..7589475d87 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/device-profile-config.component.ts @@ -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-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}); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/device-state-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/device-state-config.component.html new file mode 100644 index 0000000000..cae70173ff --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/device-state-config.component.html @@ -0,0 +1,27 @@ + +
    + + {{ 'tb.rulenode.select-device-connectivity-event' | translate }} + + + {{ messageTypeNames.get(eventOption) }} + + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/device-state-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/device-state-config.component.ts new file mode 100644 index 0000000000..635b10f41b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/device-state-config.component.ts @@ -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 { 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]] + }); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/generator-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/generator-config.component.html new file mode 100644 index 0000000000..a3ef3821bd --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/generator-config.component.html @@ -0,0 +1,117 @@ + +
    +
    +
    tb.rulenode.generation-parameters
    +
    + + tb.rulenode.message-count + + + {{ 'tb.rulenode.message-count-required' | translate }} + + + {{ 'tb.rulenode.min-message-count-message' | translate }} + + + + tb.rulenode.generation-frequency-seconds + + + {{ 'tb.rulenode.generation-frequency-required' | translate }} + + + {{ 'tb.rulenode.min-generation-frequency-message' | translate }} + + +
    +
    + +
    +
    tb.rulenode.originator
    + + +
    +
    + + + tb.rulenode.generator-function + + + + + {{ 'tb.rulenode.script-lang-tbel' | translate }} + + + {{ 'tb.rulenode.script-lang-js' | translate }} + + + + + + + + {{ 'tb.rulenode.script-lang-tbel' | translate }} + + + {{ 'tb.rulenode.script-lang-js' | translate }} + + + + +
    + +
    +
    +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/generator-config.component.scss b/ui-ngx/src/app/modules/home/components/rule-node/action/generator-config.component.scss new file mode 100644 index 0000000000..93c7934484 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/generator-config.component.scss @@ -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; + } + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/generator-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/generator-config.component.ts new file mode 100644 index 0000000000..0524cfe546 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/generator-config.component.ts @@ -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 = new EventEmitter(); + + allowedEntityTypes = [ + EntityType.DEVICE, EntityType.ASSET, EntityType.ENTITY_VIEW, EntityType.CUSTOMER, + EntityType.USER, EntityType.DASHBOARD + ]; + + additionEntityTypes = { + TENANT: this.translate.instant('tb.rulenode.current-tenant'), + RULE_NODE: this.translate.instant('tb.rulenode.current-rule-node') + }; + + readonly hasScript = true; + + readonly testScriptLabel = 'tb.rulenode.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('tb.rulenode.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(); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/gps-geo-action-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/gps-geo-action-config.component.html new file mode 100644 index 0000000000..e76192aa9d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/gps-geo-action-config.component.html @@ -0,0 +1,195 @@ + +
    +
    +
    tb.rulenode.coordinate-field-names
    +
    +
    + + {{ 'tb.rulenode.latitude-field-name' | translate }} + + + {{ 'tb.rulenode.latitude-field-name-required' | translate }} + + + + {{ 'tb.rulenode.longitude-field-name' | translate }} + + + {{ 'tb.rulenode.longitude-field-name-required' | translate }} + + +
    +
    tb.rulenode.coordinate-field-hint
    +
    +
    +
    +
    tb.rulenode.geofence-configuration
    +
    + + {{ 'tb.rulenode.perimeter-type' | translate }} + + + {{ perimeterTypeTranslationMap.get(type) | translate }} + + + +
    + + {{ 'tb.rulenode.fetch-perimeter-info-from-metadata' | translate }} + +
    + + {{ 'tb.rulenode.perimeter-key-name' | translate }} + + + {{ 'tb.rulenode.perimeter-key-name-required' | translate }} + + {{ 'tb.rulenode.perimeter-key-name-hint' | translate }} + +
    +
    + + {{ 'tb.rulenode.circle-center-latitude' | translate }} + + + {{ 'tb.rulenode.circle-center-latitude-required' | translate }} + + + + {{ 'tb.rulenode.circle-center-longitude' | translate }} + + + {{ 'tb.rulenode.circle-center-longitude-required' | translate }} + + +
    +
    + + {{ 'tb.rulenode.range' | translate }} + + + {{ 'tb.rulenode.range-required' | translate }} + + + + {{ 'tb.rulenode.range-units' | translate }} + + + {{ rangeUnitTranslationMap.get(type) | translate }} + + + + {{ 'tb.rulenode.range-units-required' | translate }} + + +
    +
    +
    + + tb.rulenode.polygon-definition + + + help + + + {{ 'tb.rulenode.polygon-definition-required' | translate }} + + +
    +
    +
    +
    +
    +
    {{ 'tb.rulenode.presence-monitoring-strategy' | translate }}
    + + + {{ presenceMonitoringStrategies.get(strategy).name | translate }} + + +
    +
    {{ geoActionConfigForm.get('reportPresenceStatusOnEachMessage').value === false ? + ('tb.rulenode.presence-monitoring-strategy-on-first-message-hint' | translate) : + ('tb.rulenode.presence-monitoring-strategy-on-each-message-hint' | translate) }} +
    +
    +
    +
    + + tb.rulenode.min-inside-duration + + + {{ 'tb.rulenode.min-inside-duration-value-required' | translate }} + + + {{ 'tb.rulenode.time-value-range' | translate }} + + + {{ 'tb.rulenode.time-value-range' | translate }} + + + + tb.rulenode.min-inside-duration-time-unit + + + {{ timeUnitsTranslationMap.get(timeUnit) | translate }} + + + +
    +
    + + tb.rulenode.min-outside-duration + + + {{ 'tb.rulenode.min-outside-duration-value-required' | translate }} + + + {{ 'tb.rulenode.time-value-range' | translate }} + + + {{ 'tb.rulenode.time-value-range' | translate }} + + + + tb.rulenode.min-outside-duration-time-unit + + + {{ timeUnitsTranslationMap.get(timeUnit) | translate }} + + + +
    +
    +
    +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/gps-geo-action-config.component.scss b/ui-ngx/src/app/modules/home/components/rule-node/action/gps-geo-action-config.component.scss new file mode 100644 index 0000000000..f177555e37 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/gps-geo-action-config.component.scss @@ -0,0 +1,20 @@ +/** + * 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 { + .slide-toggle { + margin-bottom: 18px; + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/gps-geo-action-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/gps-geo-action-config.component.ts new file mode 100644 index 0000000000..47347edadf --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/gps-geo-action-config.component.ts @@ -0,0 +1,126 @@ +/// +/// 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 { + PerimeterType, + perimeterTypeTranslations, + PresenceMonitoringStrategiesData, + RangeUnit, + rangeUnitTranslations, + TimeUnit, + timeUnitTranslations +} from '../rule-node-config.models'; + +@Component({ + selector: 'tb-action-node-gps-geofencing-config', + templateUrl: './gps-geo-action-config.component.html', + styleUrls: ['./gps-geo-action-config.component.scss'] +}) +export class GpsGeoActionConfigComponent extends RuleNodeConfigurationComponent { + + geoActionConfigForm: UntypedFormGroup; + + perimeterType = PerimeterType; + perimeterTypes = Object.keys(PerimeterType); + perimeterTypeTranslationMap = perimeterTypeTranslations; + + rangeUnits = Object.keys(RangeUnit); + rangeUnitTranslationMap = rangeUnitTranslations; + + presenceMonitoringStrategies = PresenceMonitoringStrategiesData; + presenceMonitoringStrategyKeys = Array.from(this.presenceMonitoringStrategies.keys()); + + timeUnits = Object.keys(TimeUnit); + timeUnitsTranslationMap = timeUnitTranslations; + + public defaultPaddingEnable = true; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.geoActionConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.geoActionConfigForm = this.fb.group({ + reportPresenceStatusOnEachMessage: [configuration ? configuration.reportPresenceStatusOnEachMessage : true, + [Validators.required]], + latitudeKeyName: [configuration ? configuration.latitudeKeyName : null, [Validators.required]], + longitudeKeyName: [configuration ? configuration.longitudeKeyName : null, [Validators.required]], + perimeterType: [configuration ? configuration.perimeterType : null, [Validators.required]], + fetchPerimeterInfoFromMessageMetadata: [configuration ? configuration.fetchPerimeterInfoFromMessageMetadata : false, []], + perimeterKeyName: [configuration ? configuration.perimeterKeyName : null, []], + centerLatitude: [configuration ? configuration.centerLatitude : null, []], + centerLongitude: [configuration ? configuration.centerLatitude : null, []], + range: [configuration ? configuration.range : null, []], + rangeUnit: [configuration ? configuration.rangeUnit : null, []], + polygonsDefinition: [configuration ? configuration.polygonsDefinition : null, []], + minInsideDuration: [configuration ? configuration.minInsideDuration : null, + [Validators.required, Validators.min(1), Validators.max(2147483647)]], + minInsideDurationTimeUnit: [configuration ? configuration.minInsideDurationTimeUnit : null, [Validators.required]], + minOutsideDuration: [configuration ? configuration.minOutsideDuration : null, + [Validators.required, Validators.min(1), Validators.max(2147483647)]], + minOutsideDurationTimeUnit: [configuration ? configuration.minOutsideDurationTimeUnit : null, [Validators.required]], + }); + } + + protected validatorTriggers(): string[] { + return ['fetchPerimeterInfoFromMessageMetadata', 'perimeterType']; + } + + protected updateValidators(emitEvent: boolean) { + const fetchPerimeterInfoFromMessageMetadata: boolean = this.geoActionConfigForm.get('fetchPerimeterInfoFromMessageMetadata').value; + const perimeterType: PerimeterType = this.geoActionConfigForm.get('perimeterType').value; + if (fetchPerimeterInfoFromMessageMetadata) { + this.geoActionConfigForm.get('perimeterKeyName').setValidators([Validators.required]); + } else { + this.geoActionConfigForm.get('perimeterKeyName').setValidators([]); + } + if (!fetchPerimeterInfoFromMessageMetadata && perimeterType === PerimeterType.CIRCLE) { + this.geoActionConfigForm.get('centerLatitude').setValidators([Validators.required, + Validators.min(-90), Validators.max(90)]); + this.geoActionConfigForm.get('centerLongitude').setValidators([Validators.required, + Validators.min(-180), Validators.max(180)]); + this.geoActionConfigForm.get('range').setValidators([Validators.required, Validators.min(0)]); + this.geoActionConfigForm.get('rangeUnit').setValidators([Validators.required]); + + this.defaultPaddingEnable = false; + } else { + this.geoActionConfigForm.get('centerLatitude').setValidators([]); + this.geoActionConfigForm.get('centerLongitude').setValidators([]); + this.geoActionConfigForm.get('range').setValidators([]); + this.geoActionConfigForm.get('rangeUnit').setValidators([]); + + this.defaultPaddingEnable = true; + } + if (!fetchPerimeterInfoFromMessageMetadata && perimeterType === PerimeterType.POLYGON) { + this.geoActionConfigForm.get('polygonsDefinition').setValidators([Validators.required]); + } else { + this.geoActionConfigForm.get('polygonsDefinition').setValidators([]); + } + this.geoActionConfigForm.get('perimeterKeyName').updateValueAndValidity({emitEvent}); + this.geoActionConfigForm.get('centerLatitude').updateValueAndValidity({emitEvent}); + this.geoActionConfigForm.get('centerLongitude').updateValueAndValidity({emitEvent}); + this.geoActionConfigForm.get('range').updateValueAndValidity({emitEvent}); + this.geoActionConfigForm.get('rangeUnit').updateValueAndValidity({emitEvent}); + this.geoActionConfigForm.get('polygonsDefinition').updateValueAndValidity({emitEvent}); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/log-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/log-config.component.html new file mode 100644 index 0000000000..c47644ae5f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/log-config.component.html @@ -0,0 +1,59 @@ + +
    + + + + + + + +
    + +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/log-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/log-config.component.ts new file mode 100644 index 0000000000..dab86ae14d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/log-config.component.ts @@ -0,0 +1,124 @@ +/// +/// 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, 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 { DebugRuleNodeEventBody } from '@shared/models/event.models'; + +@Component({ + selector: 'tb-action-node-log-config', + templateUrl: './log-config.component.html', + styleUrls: [] +}) +export class LogConfigComponent extends RuleNodeConfigurationComponent { + + @ViewChild('jsFuncComponent', {static: false}) jsFuncComponent: JsFuncComponent; + @ViewChild('tbelFuncComponent', {static: false}) tbelFuncComponent: JsFuncComponent; + + logConfigForm: UntypedFormGroup; + + tbelEnabled = getCurrentAuthState(this.store).tbelEnabled; + + scriptLanguage = ScriptLanguage; + + changeScript: EventEmitter = new EventEmitter(); + + readonly hasScript = true; + + readonly testScriptLabel = 'tb.rulenode.test-to-string-function'; + + constructor(private fb: UntypedFormBuilder, + private nodeScriptTestService: NodeScriptTestService, + private translate: TranslateService) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.logConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.logConfigForm = this.fb.group({ + 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.logConfigForm.get('scriptLang').value; + if (scriptLang === ScriptLanguage.TBEL && !this.tbelEnabled) { + scriptLang = ScriptLanguage.JS; + this.logConfigForm.get('scriptLang').patchValue(scriptLang, {emitEvent: false}); + setTimeout(() => {this.logConfigForm.updateValueAndValidity({emitEvent: true});}); + } + this.logConfigForm.get('jsScript').setValidators(scriptLang === ScriptLanguage.JS ? [Validators.required] : []); + this.logConfigForm.get('jsScript').updateValueAndValidity({emitEvent}); + this.logConfigForm.get('tbelScript').setValidators(scriptLang === ScriptLanguage.TBEL ? [Validators.required] : []); + this.logConfigForm.get('tbelScript').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.logConfigForm.get('scriptLang').value; + const scriptField = scriptLang === ScriptLanguage.JS ? 'jsScript' : 'tbelScript'; + const helpId = scriptLang === ScriptLanguage.JS ? 'rulenode/log_node_script_fn' : 'rulenode/tbel/log_node_script_fn'; + const script: string = this.logConfigForm.get(scriptField).value; + this.nodeScriptTestService.testNodeScript( + script, + 'string', + this.translate.instant('tb.rulenode.to-string'), + 'ToString', + ['msg', 'metadata', 'msgType'], + this.ruleNodeId, + helpId, + scriptLang, + debugEventBody + ).subscribe((theScript) => { + if (theScript) { + this.logConfigForm.get(scriptField).setValue(theScript); + this.changeScript.emit(); + } + }); + } + + protected onValidate() { + const scriptLang: ScriptLanguage = this.logConfigForm.get('scriptLang').value; + if (scriptLang === ScriptLanguage.JS) { + this.jsFuncComponent.validateOnSubmit(); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/math-function-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/math-function-config.component.html new file mode 100644 index 0000000000..4da207aacb --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/math-function-config.component.html @@ -0,0 +1,103 @@ + +
    + + +
    + tb.rulenode.argument-tile + + +
    +
    + {{'tb.rulenode.custom-expression-field-input' | translate }} * + + + + tb.rulenode.custom-expression-field-input-required + + tb.rulenode.custom-expression-field-input-hint + +
    +
    + tb.rulenode.result-title +
    + + tb.rulenode.type-field-input + + + {{ argumentTypeResultMap.get(mathFunctionConfigForm.get('result.type').value)?.name | translate }} + + + {{ argumentTypeResultMap.get(argument).name | translate }} + + {{ argumentTypeResultMap.get(argument).description }} + + + + + tb.rulenode.type-field-input-required + + +
    + + tb.rulenode.attribute-scope-field-input + + + {{ attributeScopeMap.get(scope) | translate }} + + + + + tb.rulenode.key-field-input + + help + + tb.rulenode.key-field-input-required + + +
    +
    + + tb.rulenode.number-floating-point-field-input + + + +
    +
    + + {{'tb.rulenode.add-to-message-field-input' | translate }} + + + {{'tb.rulenode.add-to-metadata-field-input' | translate}} + +
    +
    +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/math-function-config.component.scss b/ui-ngx/src/app/modules/home/components/rule-node/action/math-function-config.component.scss new file mode 100644 index 0000000000..d96fbe7578 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/math-function-config.component.scss @@ -0,0 +1,41 @@ +/** + * 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 { + .fields-group { + padding: 0 16px 8px; + margin: 10px 0; + border: 1px groove rgba(0, 0, 0, .25); + border-radius: 4px; + + .mat-mdc-form-field { + .mat-mdc-form-field-infix { + width: 100%; + } + } + + legend { + color: rgba(0, 0, 0, .7); + width: fit-content; + } + + legend + * { + display: block; + &.no-margin-top { + margin-top: 0; + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/math-function-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/math-function-config.component.ts new file mode 100644 index 0000000000..2ff796840e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/math-function-config.component.ts @@ -0,0 +1,92 @@ +/// +/// 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 { + ArgumentTypeResult, + ArgumentTypeResultMap, + AttributeScopeMap, + AttributeScopeResult, + MathFunction +} from '../rule-node-config.models'; + + +@Component({ + selector: 'tb-action-node-math-function-config', + templateUrl: './math-function-config.component.html', + styleUrls: ['./math-function-config.component.scss'] +}) +export class MathFunctionConfigComponent extends RuleNodeConfigurationComponent { + + mathFunctionConfigForm: UntypedFormGroup; + + MathFunction = MathFunction; + ArgumentTypeResult = ArgumentTypeResult; + argumentTypeResultMap = ArgumentTypeResultMap; + attributeScopeMap = AttributeScopeMap; + argumentsResult = Object.values(ArgumentTypeResult); + attributeScopeResult = Object.values(AttributeScopeResult); + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.mathFunctionConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.mathFunctionConfigForm = this.fb.group({ + operation: [configuration ? configuration.operation : null, [Validators.required]], + arguments: [configuration ? configuration.arguments : null, [Validators.required]], + customFunction: [configuration ? configuration.customFunction : '', [Validators.required]], + result: this.fb.group({ + type: [configuration ? configuration.result.type: null, [Validators.required]], + attributeScope: [configuration ? configuration.result.attributeScope : null, [Validators.required]], + key: [configuration ? configuration.result.key : '', [Validators.required]], + resultValuePrecision: [configuration ? configuration.result.resultValuePrecision : 0], + addToBody: [configuration ? configuration.result.addToBody : false], + addToMetadata: [configuration ? configuration.result.addToMetadata : false] + }) + }); + } + + protected updateValidators(emitEvent: boolean) { + const operation: MathFunction = this.mathFunctionConfigForm.get('operation').value; + const resultType: ArgumentTypeResult = this.mathFunctionConfigForm.get('result.type').value; + if (operation === MathFunction.CUSTOM) { + this.mathFunctionConfigForm.get('customFunction').enable({emitEvent: false}); + if (this.mathFunctionConfigForm.get('customFunction').value === null) { + this.mathFunctionConfigForm.get('customFunction').patchValue('(x - 32) / 1.8', {emitEvent: false}); + } + } else { + this.mathFunctionConfigForm.get('customFunction').disable({emitEvent: false}); + } + if (resultType === ArgumentTypeResult.ATTRIBUTE) { + this.mathFunctionConfigForm.get('result.attributeScope').enable({emitEvent: false}); + } else { + this.mathFunctionConfigForm.get('result.attributeScope').disable({emitEvent: false}); + } + this.mathFunctionConfigForm.get('customFunction').updateValueAndValidity({emitEvent}); + this.mathFunctionConfigForm.get('result.attributeScope').updateValueAndValidity({emitEvent}); + } + + protected validatorTriggers(): string[] { + return ['operation', 'result.type']; + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/msg-count-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/msg-count-config.component.html new file mode 100644 index 0000000000..e013fc1d5d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/msg-count-config.component.html @@ -0,0 +1,36 @@ + +
    + + tb.rulenode.interval-seconds + + + {{ 'tb.rulenode.interval-seconds-required' | translate }} + + + {{ 'tb.rulenode.min-interval-seconds-message' | translate }} + + + + tb.rulenode.output-timeseries-key-prefix + + + {{ 'tb.rulenode.output-timeseries-key-prefix-required' | translate }} + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/msg-count-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/msg-count-config.component.ts new file mode 100644 index 0000000000..5380c569a0 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/msg-count-config.component.ts @@ -0,0 +1,45 @@ +/// +/// 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-msg-count-config', + templateUrl: './msg-count-config.component.html', + styleUrls: [] +}) +export class MsgCountConfigComponent extends RuleNodeConfigurationComponent { + + msgCountConfigForm: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.msgCountConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.msgCountConfigForm = this.fb.group({ + interval: [configuration ? configuration.interval : null, [Validators.required, Validators.min(1)]], + telemetryPrefix: [configuration ? configuration.telemetryPrefix : null, [Validators.required]] + }); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/msg-delay-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/msg-delay-config.component.html new file mode 100644 index 0000000000..677228b7bc --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/msg-delay-config.component.html @@ -0,0 +1,57 @@ + +
    + + {{ 'tb.rulenode.use-metadata-period-in-seconds-patterns' | translate }} + +
    tb.rulenode.use-metadata-period-in-seconds-patterns-hint
    + + tb.rulenode.period-seconds + + + {{ 'tb.rulenode.period-seconds-required' | translate }} + + + {{ 'tb.rulenode.min-period-0-seconds-message' | translate }} + + + + + tb.rulenode.period-in-seconds-pattern + + + {{ 'tb.rulenode.period-in-seconds-pattern-required' | translate }} + + tb.rulenode.general-pattern-hint + + + + tb.rulenode.max-pending-messages + + + {{ 'tb.rulenode.max-pending-messages-required' | translate }} + + + {{ 'tb.rulenode.max-pending-messages-range' | translate }} + + + {{ 'tb.rulenode.max-pending-messages-range' | translate }} + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/msg-delay-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/msg-delay-config.component.ts new file mode 100644 index 0000000000..cabf2e67af --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/msg-delay-config.component.ts @@ -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'; + +@Component({ + selector: 'tb-action-node-msg-delay-config', + templateUrl: './msg-delay-config.component.html', + styleUrls: [] +}) +export class MsgDelayConfigComponent extends RuleNodeConfigurationComponent { + + msgDelayConfigForm: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.msgDelayConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.msgDelayConfigForm = this.fb.group({ + useMetadataPeriodInSecondsPatterns: [configuration ? configuration.useMetadataPeriodInSecondsPatterns : false, []], + periodInSeconds: [configuration ? configuration.periodInSeconds : null, []], + periodInSecondsPattern: [configuration ? configuration.periodInSecondsPattern : null, []], + maxPendingMsgs: [configuration ? configuration.maxPendingMsgs : null, + [Validators.required, Validators.min(1), Validators.max(100000)]], + }); + } + + protected validatorTriggers(): string[] { + return ['useMetadataPeriodInSecondsPatterns']; + } + + protected updateValidators(emitEvent: boolean) { + const useMetadataPeriodInSecondsPatterns: boolean = this.msgDelayConfigForm.get('useMetadataPeriodInSecondsPatterns').value; + if (useMetadataPeriodInSecondsPatterns) { + this.msgDelayConfigForm.get('periodInSecondsPattern').setValidators([Validators.required]); + this.msgDelayConfigForm.get('periodInSeconds').setValidators([]); + } else { + this.msgDelayConfigForm.get('periodInSecondsPattern').setValidators([]); + this.msgDelayConfigForm.get('periodInSeconds').setValidators([Validators.required, Validators.min(0)]); + } + this.msgDelayConfigForm.get('periodInSecondsPattern').updateValueAndValidity({emitEvent}); + this.msgDelayConfigForm.get('periodInSeconds').updateValueAndValidity({emitEvent}); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/push-to-cloud-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/push-to-cloud-config.component.html new file mode 100644 index 0000000000..8e37d85c47 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/push-to-cloud-config.component.html @@ -0,0 +1,49 @@ + +
    +
    + + +
    + + {{ 'tb.rulenode.attributes-scope' | translate }} + + + {{ telemetryTypeTranslationsMap.get(scope) | translate }} + + + + + {{ 'tb.rulenode.attributes-scope-value' | translate }} + + + +
    +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/push-to-cloud-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/push-to-cloud-config.component.ts new file mode 100644 index 0000000000..07082ebabe --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/push-to-cloud-config.component.ts @@ -0,0 +1,48 @@ +/// +/// 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 '@shared/models/telemetry/telemetry.models'; + +@Component({ + selector: 'tb-action-node-push-to-cloud-config', + templateUrl: './push-to-cloud-config.component.html', + styleUrls: [] +}) +export class PushToCloudConfigComponent extends RuleNodeConfigurationComponent { + + attributeScopes = Object.keys(AttributeScope); + telemetryTypeTranslationsMap = telemetryTypeTranslations; + + pushToCloudConfigForm: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.pushToCloudConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.pushToCloudConfigForm = this.fb.group({ + scope: [configuration ? configuration.scope : null, [Validators.required]] + }); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/push-to-edge-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/push-to-edge-config.component.html new file mode 100644 index 0000000000..6c3088fc23 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/push-to-edge-config.component.html @@ -0,0 +1,49 @@ + +
    +
    + + +
    + + {{ 'tb.rulenode.attributes-scope' | translate }} + + + {{ telemetryTypeTranslationsMap.get(scope) | translate }} + + + + + {{ 'tb.rulenode.attributes-scope-value' | translate }} + + + +
    +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/push-to-edge-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/push-to-edge-config.component.ts new file mode 100644 index 0000000000..bbefcd3d76 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/push-to-edge-config.component.ts @@ -0,0 +1,48 @@ +/// +/// 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 '@shared/models/telemetry/telemetry.models'; + +@Component({ + selector: 'tb-action-node-push-to-edge-config', + templateUrl: './push-to-edge-config.component.html', + styleUrls: [] +}) +export class PushToEdgeConfigComponent extends RuleNodeConfigurationComponent { + + attributeScopes = Object.keys(AttributeScope); + telemetryTypeTranslationsMap = telemetryTypeTranslations; + + pushToEdgeConfigForm: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.pushToEdgeConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.pushToEdgeConfigForm = this.fb.group({ + scope: [configuration ? configuration.scope : null, [Validators.required]] + }); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/rpc-reply-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/rpc-reply-config.component.html new file mode 100644 index 0000000000..b4ec4b5047 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/rpc-reply-config.component.html @@ -0,0 +1,36 @@ + +
    +
    tb.rulenode.reply-routing-configuration
    + + +
    + + tb.rulenode.service-id-metadata-attribute + + + + tb.rulenode.session-id-metadata-attribute + + + + tb.rulenode.request-id-metadata-attribute + + +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/rpc-reply-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/rpc-reply-config.component.ts new file mode 100644 index 0000000000..840c6eb9c3 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/rpc-reply-config.component.ts @@ -0,0 +1,45 @@ +/// +/// 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-rpc-reply-config', + templateUrl: './rpc-reply-config.component.html', + styleUrls: [] +}) +export class RpcReplyConfigComponent extends RuleNodeConfigurationComponent { + + rpcReplyConfigForm: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.rpcReplyConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.rpcReplyConfigForm = this.fb.group({ + serviceIdMetaDataAttribute: [configuration ? configuration.serviceIdMetaDataAttribute : null, []], + sessionIdMetaDataAttribute: [configuration ? configuration.sessionIdMetaDataAttribute : null, []], + requestIdMetaDataAttribute: [configuration ? configuration.requestIdMetaDataAttribute : null, []] + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/rpc-request-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/rpc-request-config.component.html new file mode 100644 index 0000000000..2673c7e9f3 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/rpc-request-config.component.html @@ -0,0 +1,29 @@ + +
    + + tb.rulenode.timeout-sec + + + {{ 'tb.rulenode.timeout-required' | translate }} + + + {{ 'tb.rulenode.min-timeout-message' | translate }} + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/rpc-request-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/rpc-request-config.component.ts new file mode 100644 index 0000000000..d54677b237 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/rpc-request-config.component.ts @@ -0,0 +1,43 @@ +/// +/// 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-rpc-request-config', + templateUrl: './rpc-request-config.component.html', + styleUrls: [] +}) +export class RpcRequestConfigComponent extends RuleNodeConfigurationComponent { + + rpcRequestConfigForm: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.rpcRequestConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.rpcRequestConfigForm = this.fb.group({ + timeoutInSeconds: [configuration ? configuration.timeoutInSeconds : null, [Validators.required, Validators.min(0)]] + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/rule-node-config-action.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/rule-node-config-action.module.ts new file mode 100644 index 0000000000..8c8350378d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/rule-node-config-action.module.ts @@ -0,0 +1,132 @@ +/// +/// 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, Type } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { IRuleNodeConfigurationComponent, 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 { RuleNodeConfigCommonModule } from '../common/rule-node-config-common.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'; +import { EmptyConfigComponent } from '@home/components/rule-node/empty-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, + RuleNodeConfigCommonModule + ], + 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 RuleNodeConfigActionModule { +} + +export const ruleNodeActionConfigComponentsMap: Record> = { + 'tbActionNodeAssignToCustomerConfig': AssignCustomerConfigComponent, + 'tbActionNodeAttributesConfig': AttributesConfigComponent, + 'tbActionNodeClearAlarmConfig': ClearAlarmConfigComponent, + 'tbActionNodeCreateAlarmConfig': CreateAlarmConfigComponent, + 'tbActionNodeCreateRelationConfig': CreateRelationConfigComponent, + 'tbActionNodeDeleteAttributesConfig': DeleteAttributesConfigComponent, + 'tbActionNodeDeleteRelationConfig': DeleteRelationConfigComponent, + 'tbDeviceProfileConfig': DeviceProfileConfigComponent, + 'tbActionNodeDeviceStateConfig': DeviceStateConfigComponent, + 'tbActionNodeGeneratorConfig': GeneratorConfigComponent, + 'tbActionNodeGpsGeofencingConfig': GpsGeoActionConfigComponent, + 'tbActionNodeLogConfig': LogConfigComponent, + 'tbActionNodeMathFunctionConfig': MathFunctionConfigComponent, + 'tbActionNodeMsgCountConfig': MsgCountConfigComponent, + 'tbActionNodeMsgDelayConfig': MsgDelayConfigComponent, + 'tbActionNodePushToCloudConfig': PushToCloudConfigComponent, + 'tbActionNodePushToEdgeConfig': PushToEdgeConfigComponent, + 'tbActionNodeRpcReplyConfig': RpcReplyConfigComponent, + 'tbActionNodeRpcRequestConfig': RpcRequestConfigComponent, + 'tbActionNodeCustomTableConfig': SaveToCustomTableConfigComponent, + 'tbActionNodeSendRestApiCallReplyConfig': SendRestApiCallReplyConfigComponent, + 'tbActionNodeTimeseriesConfig': TimeseriesConfigComponent, + 'tbActionNodeUnAssignToCustomerConfig': UnassignCustomerConfigComponent +}; diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/save-to-custom-table-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/save-to-custom-table-config.component.html new file mode 100644 index 0000000000..61790d8537 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/save-to-custom-table-config.component.html @@ -0,0 +1,56 @@ + +
    + + tb.rulenode.custom-table-name + + + help + + + {{ 'tb.rulenode.custom-table-name-required' | translate }} + + + + + + tb.rulenode.default-ttl + + tb.rulenode.default-ttl-zero-hint + + {{ 'tb.rulenode.min-default-ttl-message' | translate }} + + + {{ 'tb.rulenode.default-ttl-required' | translate }} + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/save-to-custom-table-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/save-to-custom-table-config.component.ts new file mode 100644 index 0000000000..27d40dd4b9 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/save-to-custom-table-config.component.ts @@ -0,0 +1,50 @@ +/// +/// 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-custom-table-config', + templateUrl: './save-to-custom-table-config.component.html', + styleUrls: [] +}) +export class SaveToCustomTableConfigComponent extends RuleNodeConfigurationComponent { + + saveToCustomTableConfigForm: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.saveToCustomTableConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.saveToCustomTableConfigForm = this.fb.group({ + tableName: [configuration ? configuration.tableName : null, [Validators.required, Validators.pattern(/.*\S.*/)]], + fieldsMapping: [configuration ? configuration.fieldsMapping : null, [Validators.required]], + defaultTtl: [configuration ? configuration.defaultTtl : 0, [Validators.required, Validators.min(0)]] + }); + } + + protected prepareOutputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + configuration.tableName = configuration.tableName.trim(); + return configuration; + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/send-rest-api-call-reply-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/send-rest-api-call-reply-config.component.html new file mode 100644 index 0000000000..b1cbce5042 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/send-rest-api-call-reply-config.component.html @@ -0,0 +1,32 @@ + +
    +
    tb.rulenode.reply-routing-configuration
    + + +
    + + tb.rulenode.service-id-metadata-attribute + + + + tb.rulenode.request-id-metadata-attribute + + +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/send-rest-api-call-reply-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/send-rest-api-call-reply-config.component.ts new file mode 100644 index 0000000000..ad4e148658 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/send-rest-api-call-reply-config.component.ts @@ -0,0 +1,44 @@ +/// +/// 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-send-rest-api-call-reply-config', + templateUrl: './send-rest-api-call-reply-config.component.html', + styleUrls: [] +}) +export class SendRestApiCallReplyConfigComponent extends RuleNodeConfigurationComponent { + + sendRestApiCallReplyConfigForm: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.sendRestApiCallReplyConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.sendRestApiCallReplyConfigForm = this.fb.group({ + requestIdMetaDataAttribute: [configuration ? configuration.requestIdMetaDataAttribute : null, []], + serviceIdMetaDataAttribute: [configuration ? configuration.serviceIdMetaDataAttribute : null, []] + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.html new file mode 100644 index 0000000000..0635232e48 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.html @@ -0,0 +1,50 @@ + +
    + + tb.rulenode.default-ttl + + + help + + + {{ 'tb.rulenode.default-ttl-required' | translate }} + + + {{ 'tb.rulenode.min-default-ttl-message' | translate }} + + +
    +
    + + {{ 'tb.rulenode.use-server-ts' | translate }} + +
    +
    + + {{ 'tb.rulenode.skip-latest-persistence' | translate }} + +
    +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.ts new file mode 100644 index 0000000000..d480ec4d39 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.ts @@ -0,0 +1,45 @@ +/// +/// 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-timeseries-config', + templateUrl: './timeseries-config.component.html', + styleUrls: [] +}) +export class TimeseriesConfigComponent extends RuleNodeConfigurationComponent { + + timeseriesConfigForm: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.timeseriesConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.timeseriesConfigForm = this.fb.group({ + defaultTTL: [configuration ? configuration.defaultTTL : null, [Validators.required, Validators.min(0)]], + skipLatestPersistence: [configuration ? configuration.skipLatestPersistence : false, []], + useServerTs: [configuration ? configuration.useServerTs : false, []] + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/unassign-customer-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/unassign-customer-config.component.html new file mode 100644 index 0000000000..94d2447d19 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/unassign-customer-config.component.html @@ -0,0 +1,39 @@ + +
    +
    + +
    +
    + + {{ 'tb.rulenode.unassign-from-customer' | translate }} + +
    + + tb.rulenode.customer-name-pattern + + + {{ 'tb.rulenode.customer-name-pattern-required' | translate }} + + tb.rulenode.customer-name-pattern-hint + +
    +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/unassign-customer-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/unassign-customer-config.component.ts new file mode 100644 index 0000000000..a514a5b495 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/unassign-customer-config.component.ts @@ -0,0 +1,72 @@ +/// +/// 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 { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models'; + +@Component({ + selector: 'tb-action-node-un-assign-to-customer-config', + templateUrl: './unassign-customer-config.component.html', + styleUrls: [] +}) +export class UnassignCustomerConfigComponent extends RuleNodeConfigurationComponent { + + unassignCustomerConfigForm: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.unassignCustomerConfigForm; + } + + protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + return { + customerNamePattern: isDefinedAndNotNull(configuration?.customerNamePattern) ? configuration.customerNamePattern : null, + unassignFromCustomer: isDefinedAndNotNull(configuration?.customerNamePattern), + }; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.unassignCustomerConfigForm = this.fb.group({ + customerNamePattern: [configuration.customerNamePattern , []], + unassignFromCustomer: [configuration.unassignFromCustomer, []] + }); + } + + protected validatorTriggers(): string[] { + return ['unassignFromCustomer']; + } + + protected updateValidators(emitEvent: boolean) { + const unassignFromCustomer: boolean = this.unassignCustomerConfigForm.get('unassignFromCustomer').value; + if (unassignFromCustomer) { + this.unassignCustomerConfigForm.get('customerNamePattern').setValidators([Validators.required, Validators.pattern(/.*\S.*/)]); + } else { + this.unassignCustomerConfigForm.get('customerNamePattern').setValidators([]); + } + this.unassignCustomerConfigForm.get('customerNamePattern').updateValueAndValidity({emitEvent}); + } + + protected prepareOutputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + return { + customerNamePattern: configuration.unassignFromCustomer ? configuration.customerNamePattern.trim() : null + }; + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/alarm-status-select.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/alarm-status-select.component.html new file mode 100644 index 0000000000..3406218f10 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/alarm-status-select.component.html @@ -0,0 +1,37 @@ + +
    + +
    + + {{ alarmStatusTranslations.get(alarmStatus.ACTIVE_UNACK) | translate }} + + + {{ alarmStatusTranslations.get(alarmStatus.ACTIVE_ACK) | translate }} + +
    +
    + + {{ alarmStatusTranslations.get(alarmStatus.CLEARED_UNACK) | translate }} + + + {{ alarmStatusTranslations.get(alarmStatus.CLEARED_ACK) | translate }} + +
    +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/alarm-status-select.component.scss b/ui-ngx/src/app/modules/home/components/rule-node/common/alarm-status-select.component.scss new file mode 100644 index 0000000000..4eca381bff --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/alarm-status-select.component.scss @@ -0,0 +1,60 @@ +/** + * 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 { + .chip-listbox { + max-width: 460px; + width: 100%; + + .toggle-column { + display: flex; + flex: 1 1 100%; + gap: 8px; + } + + .option { + margin: 0; + } + } + + @media screen and (max-width: 959px) { + .chip-listbox { + max-width: 360px; + + .toggle-column { + flex-direction: column; + } + } + } +} + +:host ::ng-deep { + .chip-listbox { + .mdc-evolution-chip-set__chips { + gap: 8px; + } + + .option { + button { + flex-basis: 100%; + justify-content: start; + } + + .mdc-evolution-chip__graphic { + flex-grow: 0; + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/alarm-status-select.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/alarm-status-select.component.ts new file mode 100644 index 0000000000..923a4f13fc --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/alarm-status-select.component.ts @@ -0,0 +1,83 @@ +/// +/// 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, forwardRef, OnDestroy, OnInit } from '@angular/core'; +import { AlarmStatus, alarmStatusTranslations, PageComponent } from '@shared/public-api'; +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; + +@Component({ + selector: 'tb-alarm-status-select', + templateUrl: './alarm-status-select.component.html', + styleUrls: ['./alarm-status-select.component.scss'], + providers: [{ + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => AlarmStatusSelectComponent), + multi: true + }] +}) + +export class AlarmStatusSelectComponent extends PageComponent implements OnInit, ControlValueAccessor, OnDestroy { + + public alarmStatusGroup: FormGroup; + + private propagateChange = null; + private destroy$ = new Subject(); + + readonly alarmStatus = AlarmStatus; + readonly alarmStatusTranslations = alarmStatusTranslations; + + constructor(private fb: FormBuilder) { + super(); + } + + ngOnInit(): void { + this.alarmStatusGroup = this.fb.group({ + alarmStatus: [null, []] + }); + + this.alarmStatusGroup.get('alarmStatus').valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe((value) => { + this.propagateChange(value); + }); + } + + setDisabledState(isDisabled: boolean): void { + if (isDisabled) { + this.alarmStatusGroup.disable({emitEvent: false}); + } else { + this.alarmStatusGroup.enable({emitEvent: false}); + } + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } + + writeValue(value: Array): void { + this.alarmStatusGroup.get('alarmStatus').patchValue(value, {emitEvent: false}); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/arguments-map-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/arguments-map-config.component.html new file mode 100644 index 0000000000..e19a8a1408 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/arguments-map-config.component.html @@ -0,0 +1,129 @@ + +
    + +
    + + +
    + +
    + {{argumentControl.get('name').value}}. +
    + + tb.rulenode.argument-source-field-input + + + {{ argumentTypeMap.get(argumentControl.get('type').value)?.name | translate }} + + + {{ argumentTypeMap.get(argument).name | translate }} + + {{ argumentTypeMap.get(argument).description }} + + + + + tb.rulenode.argument-source-field-input-required + + +
    + + tb.rulenode.argument-key-field-input + + + help + + + tb.rulenode.argument-key-field-input-required + + + + tb.rulenode.constant-value-field-input + + + tb.rulenode.constant-value-field-input-required + + + + tb.rulenode.default-value-field-input + + +
    + + tb.rulenode.attribute-scope-field-input + + + {{ attributeScopeMap.get(scope) | translate }} + + + + tb.rulenode.attribute-scope-field-input-required + + +
    + +
    +
    +
    +
    +
    +
    + tb.rulenode.no-arguments-prompt +
    + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/arguments-map-config.component.scss b/ui-ngx/src/app/modules/home/components/rule-node/common/arguments-map-config.component.scss new file mode 100644 index 0000000000..9f6893b5df --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/arguments-map-config.component.scss @@ -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. + */ +:host { + .mat-mdc-list-item.tb-argument { + border: solid rgba(0, 0, 0, .25) 1px; + border-radius: 4px; + padding: 10px 0; + margin-bottom: 16px; + } + + .arguments-list { + padding: 0px; + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/arguments-map-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/arguments-map-config.component.ts new file mode 100644 index 0000000000..3b2e99cf96 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/arguments-map-config.component.ts @@ -0,0 +1,242 @@ +/// +/// 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, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; +import { + ControlValueAccessor, + FormArray, + FormBuilder, + FormControl, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + Validator, + Validators +} from '@angular/forms'; +import { PageComponent } from '@shared/public-api'; +import { Subscription } from 'rxjs'; +import { CdkDragDrop } from '@angular/cdk/drag-drop'; +import { + ArgumentName, + ArgumentType, + ArgumentTypeMap, + AttributeScope, + AttributeScopeMap, + MathFunction, + MathFunctionMap +} from './../rule-node-config.models'; + +@Component({ + selector: 'tb-arguments-map-config', + templateUrl: './arguments-map-config.component.html', + styleUrls: ['./arguments-map-config.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ArgumentsMapConfigComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => ArgumentsMapConfigComponent), + multi: true, + } + ] +}) +export class ArgumentsMapConfigComponent extends PageComponent implements ControlValueAccessor, OnInit, OnDestroy, Validator { + + @Input() disabled: boolean; + + private functionValue: MathFunction; + + get function(): MathFunction { + return this.functionValue; + } + + @Input() + set function(funcName: MathFunction) { + if (funcName && this.functionValue !== funcName) { + this.functionValue = funcName; + this.setupArgumentsFormGroup(true); + } + } + + maxArgs = 16; + minArgs = 1; + displayArgumentName = false; + + mathFunctionMap = MathFunctionMap; + ArgumentType = ArgumentType; + + argumentsFormGroup: FormGroup; + + attributeScopeMap = AttributeScopeMap; + argumentTypeMap = ArgumentTypeMap; + arguments = Object.values(ArgumentType); + attributeScope = Object.values(AttributeScope); + + private propagateChange = null; + + private valueChangeSubscription: Subscription[] = []; + + constructor(private fb: FormBuilder) { + super(); + } + + ngOnInit(): void { + this.argumentsFormGroup = this.fb.group({ + arguments: this.fb.array([]) + }); + + this.valueChangeSubscription.push(this.argumentsFormGroup.valueChanges.subscribe(() => { + this.updateModel(); + })); + + this.setupArgumentsFormGroup(); + } + + public onDrop(event: CdkDragDrop) { + const columnsFormArray = this.argumentsFormArray; + const columnForm = columnsFormArray.at(event.previousIndex); + columnsFormArray.removeAt(event.previousIndex); + columnsFormArray.insert(event.currentIndex, columnForm); + this.updateArgumentNames(); + } + + get argumentsFormArray(): FormArray { + return this.argumentsFormGroup.get('arguments') as FormArray; + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.argumentsFormGroup.disable({emitEvent: false}); + } else { + this.argumentsFormGroup.enable({emitEvent: false}); + this.argumentsFormArray.controls + .forEach((control: FormGroup) => this.updateArgumentControlValidators(control)); + } + } + + ngOnDestroy() { + if (this.valueChangeSubscription.length) { + this.valueChangeSubscription.forEach(sub => sub.unsubscribe()); + } + } + + writeValue(argumentsList): void { + const argumentsControls: Array = []; + if (argumentsList) { + argumentsList.forEach((property, index) => { + argumentsControls.push(this.createArgumentControl(property, index)); + }); + } + this.argumentsFormGroup.setControl('arguments', this.fb.array(argumentsControls), {emitEvent: false}); + this.setupArgumentsFormGroup(); + } + + + public removeArgument(index: number) { + this.argumentsFormArray.removeAt(index); + this.updateArgumentNames(); + } + + public addArgument(emitEvent = true) { + const argumentsFormArray = this.argumentsFormArray; + const argumentControl = this.createArgumentControl(null, argumentsFormArray.length); + argumentsFormArray.push(argumentControl, {emitEvent}); + } + + public validate(c: FormControl) { + if (!this.argumentsFormGroup.valid) { + return { + argumentsRequired: true + }; + } + return null; + } + + private setupArgumentsFormGroup(emitEvent = false) { + if (this.function) { + this.maxArgs = this.mathFunctionMap.get(this.function).maxArgs; + this.minArgs = this.mathFunctionMap.get(this.function).minArgs; + this.displayArgumentName = this.function === MathFunction.CUSTOM; + } + if (this.argumentsFormGroup) { + this.argumentsFormGroup.get('arguments').setValidators([Validators.minLength(this.minArgs), Validators.maxLength(this.maxArgs)]); + while (this.argumentsFormArray.length > this.maxArgs) { + this.removeArgument(this.maxArgs - 1); + } + while (this.argumentsFormArray.length < this.minArgs) { + this.addArgument(emitEvent); + } + this.argumentsFormGroup.get('arguments').updateValueAndValidity({emitEvent: false}); + } + } + + private createArgumentControl(property: any, index: number): FormGroup { + const argumentControl = this.fb.group({ + type: [property?.type, [Validators.required]], + key: [property?.key, [Validators.required]], + name: [ArgumentName[index], [Validators.required]], + attributeScope: [property?.attributeScope ?? AttributeScope.SERVER_SCOPE, [Validators.required]], + defaultValue: [property?.defaultValue ? property?.defaultValue : null] + }); + this.updateArgumentControlValidators(argumentControl); + this.valueChangeSubscription.push(argumentControl.get('type').valueChanges.subscribe(() => { + this.updateArgumentControlValidators(argumentControl); + argumentControl.get('attributeScope').updateValueAndValidity({emitEvent: false}); + argumentControl.get('defaultValue').updateValueAndValidity({emitEvent: false}); + })); + return argumentControl; + } + + private updateArgumentControlValidators(control: FormGroup) { + const argumentType: ArgumentType = control.get('type').value; + if (argumentType === ArgumentType.ATTRIBUTE) { + control.get('attributeScope').enable({emitEvent: false}); + } else { + control.get('attributeScope').disable({emitEvent: false}); + } + if (argumentType && argumentType !== ArgumentType.CONSTANT) { + control.get('defaultValue').enable({emitEvent: false}); + } else { + control.get('defaultValue').disable({emitEvent: false}); + } + } + + private updateArgumentNames() { + this.argumentsFormArray.controls.forEach((argumentControl, argumentIndex) => { + argumentControl.get('name').setValue(ArgumentName[argumentIndex]); + }); + } + + private updateModel() { + const argumentsForm = this.argumentsFormArray.value; + if (!argumentsForm.length || !this.argumentsFormGroup.valid) { + this.propagateChange(null); + } else { + this.propagateChange(argumentsForm); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/credentials-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/credentials-config.component.html new file mode 100644 index 0000000000..ab51ee901c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/credentials-config.component.html @@ -0,0 +1,95 @@ + +
    + + + tb.rulenode.credentials + + {{ credentialsTypeTranslationsMap.get(credentialsConfigFormGroup.get('type').value) | translate }} + + + + + tb.rulenode.credentials-type + + + {{ credentialsTypeTranslationsMap.get(credentialsType) | translate }} + + + + {{ 'tb.rulenode.credentials-type-required' | translate }} + + +
    + + + + + tb.rulenode.username + + + {{ 'tb.rulenode.username-required' | translate }} + + + + tb.rulenode.password + + + + {{ 'tb.rulenode.password-required' | translate }} + + + + +
    {{ 'tb.rulenode.credentials-pem-hint' | translate }}
    + + + + + + + + tb.rulenode.private-key-password + + + +
    +
    +
    +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/credentials-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/credentials-config.component.ts new file mode 100644 index 0000000000..97f0ba0846 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/credentials-config.component.ts @@ -0,0 +1,252 @@ +/// +/// 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, forwardRef, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + FormControl, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, + ValidatorFn, + Validators +} from '@angular/forms'; +import { AppState, isDefinedAndNotNull } from '@core/public-api'; +import { PageComponent } from '@shared/public-api'; +import { Store } from '@ngrx/store'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { credentialsType, credentialsTypes, credentialsTypeTranslations } from '../rule-node-config.models'; +import { Subscription } from 'rxjs'; + +interface CredentialsConfig { + type: credentialsType; + username?: string; + password?: string; + caCert?: string; + caCertFileName?: string; + privateKey?: string; + privateKeyFileName?: string; + cert?: string; + certFileName?: string; +} + +@Component({ + selector: 'tb-credentials-config', + templateUrl: './credentials-config.component.html', + styleUrls: [], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => CredentialsConfigComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => CredentialsConfigComponent), + multi: true, + } + ] +}) +export class CredentialsConfigComponent extends PageComponent implements ControlValueAccessor, OnInit, Validator, OnDestroy, OnChanges { + + credentialsConfigFormGroup: FormGroup; + + subscriptions: Subscription[] = []; + + private requiredValue: boolean; + + get required(): boolean { + return this.requiredValue; + } + + @Input() + set required(value: boolean) { + this.requiredValue = coerceBooleanProperty(value); + } + + @Input() + disableCertPemCredentials = false; + + @Input() + passwordFieldRequired = true; + + allCredentialsTypes = credentialsTypes; + credentialsTypeTranslationsMap = credentialsTypeTranslations; + + private propagateChange = (_: any) => {}; + + constructor(protected store: Store, + private fb: FormBuilder) { + super(store); + } + + ngOnInit(): void { + this.credentialsConfigFormGroup = this.fb.group( + { + type: [null, [Validators.required]], + username: [null, []], + password: [null, []], + caCert: [null, []], + caCertFileName: [null, []], + privateKey: [null, []], + privateKeyFileName: [null, []], + cert: [null, []], + certFileName: [null, []] + } + ); + this.subscriptions.push( + this.credentialsConfigFormGroup.valueChanges.subscribe(() => { + this.updateView(); + }) + ); + this.subscriptions.push( + this.credentialsConfigFormGroup.get('type').valueChanges.subscribe(() => { + this.credentialsTypeChanged(); + }) + ); + } + + ngOnChanges(changes: SimpleChanges): void { + for (const propName of Object.keys(changes)) { + const change = changes[propName]; + if (!change.firstChange && change.currentValue !== change.previousValue) { + if (change.currentValue && propName === 'disableCertPemCredentials') { + const credentialsTypeValue: credentialsType = this.credentialsConfigFormGroup.get('type').value; + if (credentialsTypeValue === 'cert.PEM') { + setTimeout(() => { + this.credentialsConfigFormGroup.get('type').patchValue('anonymous', {emitEvent: true}); + }); + } + } + } + } + } + + ngOnDestroy() { + this.subscriptions.forEach(s => s.unsubscribe()); + } + + writeValue(credentials: CredentialsConfig | null): void { + if (isDefinedAndNotNull(credentials)) { + this.credentialsConfigFormGroup.reset(credentials, {emitEvent: false}); + this.updateValidators(); + } + } + + setDisabledState(isDisabled: boolean): void { + if (isDisabled) { + this.credentialsConfigFormGroup.disable({emitEvent: false}); + } else { + this.credentialsConfigFormGroup.enable({emitEvent: false}); + this.updateValidators(); + } + } + + updateView() { + let credentialsConfigValue = this.credentialsConfigFormGroup.value; + const credentialsTypeValue: credentialsType = credentialsConfigValue.type; + switch (credentialsTypeValue) { + case 'anonymous': + credentialsConfigValue = { + type: credentialsTypeValue + }; + break; + case 'basic': + credentialsConfigValue = { + type: credentialsTypeValue, + username: credentialsConfigValue.username, + password: credentialsConfigValue.password, + }; + break; + case 'cert.PEM': + delete credentialsConfigValue.username; + break; + } + this.propagateChange(credentialsConfigValue); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + public validate(c: FormControl) { + return this.credentialsConfigFormGroup.valid ? null : { + credentialsConfig: { + valid: false, + }, + }; + } + + credentialsTypeChanged(): void { + this.credentialsConfigFormGroup.patchValue({ + username: null, + password: null, + caCert: null, + caCertFileName: null, + privateKey: null, + privateKeyFileName: null, + cert: null, + certFileName: null, + }); + this.updateValidators(); + } + + protected updateValidators(emitEvent: boolean = false) { + const credentialsTypeValue: credentialsType = this.credentialsConfigFormGroup.get('type').value; + if (emitEvent) { + this.credentialsConfigFormGroup.reset({type: credentialsTypeValue}, {emitEvent: false}); + } + this.credentialsConfigFormGroup.setValidators([]); + this.credentialsConfigFormGroup.get('username').setValidators([]); + this.credentialsConfigFormGroup.get('password').setValidators([]); + switch (credentialsTypeValue) { + case 'anonymous': + break; + case 'basic': + this.credentialsConfigFormGroup.get('username').setValidators([Validators.required]); + this.credentialsConfigFormGroup.get('password').setValidators(this.passwordFieldRequired ? [Validators.required] : []); + break; + case 'cert.PEM': + this.credentialsConfigFormGroup.setValidators([this.requiredFilesSelected( + Validators.required, + [['caCert', 'caCertFileName'], ['privateKey', 'privateKeyFileName', 'cert', 'certFileName']] + )]); + break; + } + this.credentialsConfigFormGroup.get('username').updateValueAndValidity({emitEvent}); + this.credentialsConfigFormGroup.get('password').updateValueAndValidity({emitEvent}); + this.credentialsConfigFormGroup.updateValueAndValidity({emitEvent}); + } + + private requiredFilesSelected(validator: ValidatorFn, + requiredFieldsSet: string[][] = null) { + return (group: FormGroup): ValidationErrors | null => { + if (!requiredFieldsSet) { + requiredFieldsSet = [Object.keys(group.controls)]; + } + const allRequiredFilesSelected = group?.controls && + requiredFieldsSet.some(arrFields => arrFields.every(k => !validator(group.controls[k]))); + + return allRequiredFilesSelected ? null : {notAllRequiredFilesSelected: true}; + }; + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/device-relations-query-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/device-relations-query-config.component.html new file mode 100644 index 0000000000..56d8c948d9 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/device-relations-query-config.component.html @@ -0,0 +1,66 @@ + +
    +
    + + relation.direction + + + {{ directionTypeTranslations.get(type) | translate }} tb.rulenode.relations-query-config-direction-suffix + + + + + tb.rulenode.max-relation-level + + + {{ 'tb.rulenode.max-relation-level-error' | translate }} + + + {{ 'tb.rulenode.max-relation-level-invalid' | translate }} + + +
    +
    + + {{ 'alias.last-level-relation' | translate }} + +
    + + + + help + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/device-relations-query-config.component.scss b/ui-ngx/src/app/modules/home/components/rule-node/common/device-relations-query-config.component.scss new file mode 100644 index 0000000000..95cfd96f20 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/device-relations-query-config.component.scss @@ -0,0 +1,20 @@ +/** + * 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 { + .last-level-slide-toggle { + margin: 8px 0 24px; + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/device-relations-query-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/device-relations-query-config.component.ts new file mode 100644 index 0000000000..23aee0b29a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/device-relations-query-config.component.ts @@ -0,0 +1,108 @@ +/// +/// 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, forwardRef, Input, OnInit } from '@angular/core'; +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { PageComponent } from '@shared/components/page.component'; +import { EntitySearchDirection, entitySearchDirectionTranslations } from '@app/shared/models/relation.models'; +import { EntityType } from '@shared/models/entity-type.models'; + +interface DeviceRelationsQuery { + fetchLastLevelOnly: boolean; + direction: EntitySearchDirection; + maxLevel?: number; + relationType?: string; + deviceTypes: string[]; +} + +@Component({ + selector: 'tb-device-relations-query-config', + templateUrl: './device-relations-query-config.component.html', + styleUrls: ['./device-relations-query-config.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => DeviceRelationsQueryConfigComponent), + multi: true + } + ] +}) +export class DeviceRelationsQueryConfigComponent extends PageComponent implements ControlValueAccessor, OnInit { + + @Input() disabled: boolean; + + private requiredValue: boolean; + + get required(): boolean { + return this.requiredValue; + } + + @Input() + set required(value: boolean) { + this.requiredValue = coerceBooleanProperty(value); + } + + directionTypes: Array = Object.values(EntitySearchDirection); + directionTypeTranslations = entitySearchDirectionTranslations; + + entityType = EntityType; + + deviceRelationsQueryFormGroup: FormGroup; + + private propagateChange = null; + + constructor(private fb: FormBuilder) { + super(); + } + + ngOnInit(): void { + this.deviceRelationsQueryFormGroup = this.fb.group({ + fetchLastLevelOnly: [false, []], + direction: [null, [Validators.required]], + maxLevel: [null, [Validators.min(1)]], + relationType: [null], + deviceTypes: [null, [Validators.required]] + }); + this.deviceRelationsQueryFormGroup.valueChanges.subscribe((query: DeviceRelationsQuery) => { + if (this.deviceRelationsQueryFormGroup.valid) { + this.propagateChange(query); + } else { + this.propagateChange(null); + } + }); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.deviceRelationsQueryFormGroup.disable({emitEvent: false}); + } else { + this.deviceRelationsQueryFormGroup.enable({emitEvent: false}); + } + } + + writeValue(query: DeviceRelationsQuery): void { + this.deviceRelationsQueryFormGroup.reset(query, {emitEvent: false}); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/example-hint.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/example-hint.component.html new file mode 100644 index 0000000000..b10c9f16b8 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/example-hint.component.html @@ -0,0 +1,29 @@ + +
    +
    +
    +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/example-hint.component.scss b/ui-ngx/src/app/modules/home/components/rule-node/common/example-hint.component.scss new file mode 100644 index 0000000000..da530c0540 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/example-hint.component.scss @@ -0,0 +1,31 @@ +/** + * 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 { + .space-between { + display: flex; + justify-content: space-between; + gap: 20px; + + .see-example { + display: flex; + flex-shrink: 0; + } + } + + .hint-text { + width: 100%; + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/example-hint.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/example-hint.component.ts new file mode 100644 index 0000000000..98c537fa0e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/example-hint.component.ts @@ -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. +/// + +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'tb-example-hint', + templateUrl: './example-hint.component.html', + styleUrls: ['./example-hint.component.scss'] +}) +export class ExampleHintComponent { + @Input() hintText: string; + + @Input() popupHelpLink: string; + + @Input() textAlign: string = 'left'; +} + + diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config-old.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config-old.component.html new file mode 100644 index 0000000000..761b9c9491 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config-old.component.html @@ -0,0 +1,70 @@ + +
    +
    + {{ keyText | translate }} + {{ valText | translate }} + +
    +
    +
    + + + + {{ keyRequiredText | translate }} + + + + + + {{ valRequiredText | translate }} + + + +
    +
    +
    + +
    + +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config-old.component.scss b/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config-old.component.scss new file mode 100644 index 0000000000..fad3641ba4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config-old.component.scss @@ -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. + */ +:host { + .tb-kv-map-config { + margin-bottom: 16px; + + .header { + padding-left: 5px; + padding-right: 5px; + padding-bottom: 5px; + + .cell { + padding-left: 5px; + padding-right: 5px; + color: #757575; + font-size: 12px; + font-weight: 700; + white-space: nowrap; + } + + .tb-required::after { + color: #757575; + font-size: 12px; + font-weight: 700; + } + } + + .body { + padding-left: 5px; + padding-right: 5px; + padding-bottom: 0; + max-height: 300px; + overflow: auto; + + .cell { + padding-left: 5px; + padding-right: 5px; + } + } + + tb-error { + display: block; + margin-top: -12px; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config-old.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config-old.component.ts new file mode 100644 index 0000000000..a08394479a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config-old.component.ts @@ -0,0 +1,198 @@ +/// +/// 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, forwardRef, Injector, Input, OnInit } from '@angular/core'; +import { + AbstractControl, + ControlValueAccessor, + FormArray, + FormBuilder, + FormControl, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + NgControl, + Validator, + Validators +} from '@angular/forms'; +import { PageComponent } from '@shared/public-api'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/public-api'; +import { Subscription } from 'rxjs'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { TranslateService } from '@ngx-translate/core'; + +@Component({ + selector: 'tb-kv-map-config-old', + templateUrl: './kv-map-config-old.component.html', + styleUrls: ['./kv-map-config-old.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => KvMapConfigOldComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => KvMapConfigOldComponent), + multi: true, + } + ] +}) +export class KvMapConfigOldComponent extends PageComponent implements ControlValueAccessor, OnInit, Validator { + + @Input() disabled: boolean; + + @Input() uniqueKeyValuePairValidator: boolean; + + @Input() requiredText: string; + + @Input() keyText: string; + + @Input() keyRequiredText: string; + + @Input() valText: string; + + @Input() valRequiredText: string; + + @Input() hintText: string; + + private requiredValue: boolean; + + get required(): boolean { + return this.requiredValue; + } + + @Input() + set required(value: boolean) { + this.requiredValue = coerceBooleanProperty(value); + } + + kvListFormGroup: FormGroup; + + ngControl: NgControl; + + private propagateChange = null; + + private valueChangeSubscription: Subscription = null; + + constructor(protected store: Store, + public translate: TranslateService, + public injector: Injector, + private fb: FormBuilder) { + super(store); + } + + ngOnInit(): void { + this.ngControl = this.injector.get(NgControl); + if (this.ngControl != null) { + this.ngControl.valueAccessor = this; + } + this.kvListFormGroup = this.fb.group({}); + this.kvListFormGroup.addControl('keyVals', + this.fb.array([])); + } + + keyValsFormArray(): FormArray { + return this.kvListFormGroup.get('keyVals') as FormArray; + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.kvListFormGroup.disable({emitEvent: false}); + } else { + this.kvListFormGroup.enable({emitEvent: false}); + } + } + + writeValue(keyValMap: { [key: string]: string }): void { + if (this.valueChangeSubscription) { + this.valueChangeSubscription.unsubscribe(); + } + const keyValsControls: Array = []; + if (keyValMap) { + for (const property of Object.keys(keyValMap)) { + if (Object.prototype.hasOwnProperty.call(keyValMap, property)) { + keyValsControls.push(this.fb.group({ + key: [property, [Validators.required]], + value: [keyValMap[property], [Validators.required]] + })); + } + } + } + this.kvListFormGroup.setControl('keyVals', this.fb.array(keyValsControls)); + this.valueChangeSubscription = this.kvListFormGroup.valueChanges.subscribe(() => { + this.updateModel(); + }); + } + + public removeKeyVal(index: number) { + (this.kvListFormGroup.get('keyVals') as FormArray).removeAt(index); + } + + public addKeyVal() { + const keyValsFormArray = this.kvListFormGroup.get('keyVals') as FormArray; + keyValsFormArray.push(this.fb.group({ + key: ['', [Validators.required]], + value: ['', [Validators.required]] + })); + } + + public validate(c: FormControl) { + const kvList: { key: string; value: string }[] = this.kvListFormGroup.get('keyVals').value; + if (!kvList.length && this.required) { + return { + kvMapRequired: true + }; + } + if (!this.kvListFormGroup.valid) { + return { + kvFieldsRequired: true + }; + } + if (this.uniqueKeyValuePairValidator) { + for (const kv of kvList) { + if (kv.key === kv.value) { + return { + uniqueKeyValuePair: true + }; + } + } + } + return null; + } + + private updateModel() { + const kvList: { key: string; value: string }[] = this.kvListFormGroup.get('keyVals').value; + if (this.required && !kvList.length || !this.kvListFormGroup.valid) { + this.propagateChange(null); + } else { + const keyValMap: { [key: string]: string } = {}; + kvList.forEach((entry) => { + keyValMap[entry.key] = entry.value; + }); + this.propagateChange(keyValMap); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config.component.html new file mode 100644 index 0000000000..36945c2211 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config.component.html @@ -0,0 +1,72 @@ + +
    +
    +
    {{ labelText }}
    +
    + {{ requiredText }} +
    +
    + tb.rulenode.map-fields-required +
    +
    + {{ 'tb.key-val.unique-key-value-pair-error' | translate: + { + valText: valText, + keyText: keyText + } }} +
    +
    +
    +
    +
    +
    {{ keyText }}
    +
    {{ valText }}
    +
    +
    +
    +
    + + + + + + +
    + +
    +
    +
    +
    +
    +
    + +
    + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config.component.scss b/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config.component.scss new file mode 100644 index 0000000000..132a1f882c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config.component.scss @@ -0,0 +1,24 @@ +/** + * 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 { + .field-space { + flex: 1 1 50%; + } + + .actions-header { + width: 40px + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config.component.ts new file mode 100644 index 0000000000..042dcf94bd --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config.component.ts @@ -0,0 +1,240 @@ +/// +/// 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, forwardRef, Injector, Input, OnDestroy, OnInit } from '@angular/core'; +import { + AbstractControl, + ControlValueAccessor, + FormArray, + FormBuilder, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + NgControl, + ValidationErrors, + Validator, + ValidatorFn, + Validators +} from '@angular/forms'; +import { coerceBoolean } from '@shared/public-api'; +import { isEqual } from '@core/public-api'; +import { Subject, takeUntil } from 'rxjs'; + +@Component({ + selector: 'tb-kv-map-config', + templateUrl: './kv-map-config.component.html', + styleUrls: ['./kv-map-config.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => KvMapConfigComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => KvMapConfigComponent), + multi: true, + } + ] +}) +export class KvMapConfigComponent implements ControlValueAccessor, OnInit, Validator, OnDestroy { + + private propagateChange: (value: any) => void = () => {}; + private destroy$ = new Subject(); + + kvListFormGroup: FormGroup; + ngControl: NgControl; + + @Input() + @coerceBoolean() + disabled = false; + + @Input() + @coerceBoolean() + uniqueKeyValuePairValidator = false; + + @Input() labelText: string; + + @Input() requiredText: string; + + @Input() keyText: string; + + @Input() keyRequiredText: string; + + @Input() valText: string; + + @Input() valRequiredText: string; + + @Input() hintText: string; + + @Input() popupHelpLink: string; + + @Input() + @coerceBoolean() + required = false; + + constructor(private injector: Injector, + private fb: FormBuilder) { + } + + ngOnInit(): void { + this.ngControl = this.injector.get(NgControl); + if (this.ngControl != null) { + this.ngControl.valueAccessor = this; + } + + this.kvListFormGroup = this.fb.group({ + keyVals: this.fb.array([]) + }, {validators: [this.propagateNestedErrors, this.oneMapRequiredValidator]}); + + this.kvListFormGroup.valueChanges + .pipe(takeUntil(this.destroy$)) + .subscribe(() => { + this.updateModel(); + }); + } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } + + keyValsFormArray(): FormArray { + return this.kvListFormGroup.get('keyVals') as FormArray; + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.kvListFormGroup.disable({emitEvent: false}); + } else { + this.kvListFormGroup.enable({emitEvent: false}); + } + } + + private duplicateValuesValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => + control.controls.key.value === control.controls.value.value + ? control.controls.key.value && control.controls.value.value ? { uniqueKeyValuePair: true } : null + : null; + + private oneMapRequiredValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => control.get('keyVals').value.length; + + + private propagateNestedErrors: ValidatorFn = (controls: FormArray | FormGroup | AbstractControl): ValidationErrors | null => { + if (this.kvListFormGroup && this.kvListFormGroup.get('keyVals') && this.kvListFormGroup.get('keyVals')?.status === 'VALID') { + return null; + } + const errors = {}; + if (this.kvListFormGroup) { + this.kvListFormGroup.setErrors(null); + } + if (controls instanceof FormArray || controls instanceof FormGroup) { + if (controls.errors) { + for (const errorKey of Object.keys(controls.errors)) { + errors[errorKey] = true; + } + } + for (const control of Object.keys(controls.controls)) { + const innerErrors = this.propagateNestedErrors(controls.controls[control]); + if (innerErrors && Object.keys(innerErrors).length) { + for (const errorKey of Object.keys(innerErrors)) { + errors[errorKey] = true; + } + } + } + return errors; + } else { + if (controls.errors) { + for (const errorKey of Object.keys(controls.errors)) { + errors[errorKey] = true; + } + } + } + + return !isEqual(errors, {}) ? errors : null; + }; + + writeValue(keyValMap: { [key: string]: string }): void { + const keyValuesData = Object.keys(keyValMap).map(key => ({key, value: keyValMap[key]})); + if (this.keyValsFormArray().length === keyValuesData.length) { + this.keyValsFormArray().patchValue(keyValuesData, {emitEvent: false}); + } else { + const keyValsControls: Array = []; + keyValuesData.forEach(data => { + keyValsControls.push(this.fb.group({ + key: [data.key, [Validators.required, Validators.pattern(/(?:.|\s)*\S(&:.|\s)*/)]], + value: [data.value, [Validators.required, Validators.pattern(/(?:.|\s)*\S(&:.|\s)*/)]] + }, {validators: this.uniqueKeyValuePairValidator ? [this.duplicateValuesValidator] : []})); + }); + this.kvListFormGroup.setControl('keyVals', this.fb.array(keyValsControls, this.propagateNestedErrors), {emitEvent: false}); + } + } + + public removeKeyVal(index: number) { + this.keyValsFormArray().removeAt(index); + } + + public addKeyVal() { + this.keyValsFormArray().push(this.fb.group({ + key: ['', [Validators.required, Validators.pattern(/(?:.|\s)*\S(&:.|\s)*/)]], + value: ['', [Validators.required, Validators.pattern(/(?:.|\s)*\S(&:.|\s)*/)]] + }, {validators: this.uniqueKeyValuePairValidator ? [this.duplicateValuesValidator] : []})); + } + + public validate() { + const kvList: { key: string; value: string }[] = this.kvListFormGroup.get('keyVals').value; + if (!kvList.length && this.required) { + return { + kvMapRequired: true + }; + } + if (!this.kvListFormGroup.valid) { + return { + kvFieldsRequired: true + }; + } + if (this.uniqueKeyValuePairValidator) { + for (const kv of kvList) { + if (kv.key === kv.value) { + return { + uniqueKeyValuePair: true + }; + } + } + } + return null; + } + + private updateModel() { + const kvList: { key: string; value: string }[] = this.kvListFormGroup.get('keyVals').value; + if (this.required && !kvList.length || !this.kvListFormGroup.valid) { + this.propagateChange(null); + } else { + const keyValMap: { [key: string]: string } = {}; + kvList.forEach((entry) => { + keyValMap[entry.key] = entry.value; + }); + this.propagateChange(keyValMap); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/math-function-autocomplete.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/math-function-autocomplete.component.html new file mode 100644 index 0000000000..6a2bf514ad --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/math-function-autocomplete.component.html @@ -0,0 +1,43 @@ + + + tb.rulenode.functions-field-input + + + + + + + {{ option.description }} + + + + tb.rulenode.no-option-found + + + diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/math-function-autocomplete.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/math-function-autocomplete.component.ts new file mode 100644 index 0000000000..1583048073 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/math-function-autocomplete.component.ts @@ -0,0 +1,152 @@ +/// +/// 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, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Observable } from 'rxjs'; +import { TranslateService } from '@ngx-translate/core'; +import { FunctionData, MathFunction, MathFunctionMap } from '../rule-node-config.models'; +import { map, tap } from 'rxjs/operators'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; + +@Component({ + selector: 'tb-math-function-autocomplete', + templateUrl: './math-function-autocomplete.component.html', + styleUrls: [], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => MathFunctionAutocompleteComponent), + multi: true + } + ] +}) +export class MathFunctionAutocompleteComponent implements ControlValueAccessor, OnInit { + + private requiredValue: boolean; + + get required(): boolean { + return this.requiredValue; + } + + @Input() + set required(value: boolean) { + this.requiredValue = coerceBooleanProperty(value); + } + + @Input() disabled: boolean; + + @ViewChild('operationInput', {static: true}) operationInput: ElementRef; + + mathFunctionForm: UntypedFormGroup; + + modelValue: MathFunction | null; + + searchText = ''; + + filteredOptions: Observable; + + private dirty = false; + + private mathOperation = [...MathFunctionMap.values()]; + + private propagateChange = null; + + constructor(public translate: TranslateService, + private fb: UntypedFormBuilder) { + } + + ngOnInit(): void { + this.mathFunctionForm = this.fb.group({ + operation: [''] + }); + this.filteredOptions = this.mathFunctionForm.get('operation').valueChanges.pipe( + tap(value => { + let modelValue; + if (typeof value === 'string' && MathFunction[value]) { + modelValue = MathFunction[value]; + } else { + modelValue = null; + } + this.updateView(modelValue); + }), + map(value => { + this.searchText = value || ''; + return value ? this._filter(value) : this.mathOperation.slice(); + }), + ); + } + + private _filter(searchText: string) { + const filterValue = searchText.toLowerCase(); + + return this.mathOperation.filter(option => option.name.toLowerCase().includes(filterValue) + || option.value.toLowerCase().includes(filterValue)); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.mathFunctionForm.disable({emitEvent: false}); + } else { + this.mathFunctionForm.enable({emitEvent: false}); + } + } + + mathFunctionDisplayFn(value: MathFunction | null) { + if (value) { + const funcData = MathFunctionMap.get(value) + return funcData.value + ' | ' + funcData.name; + } + return ''; + } + + writeValue(value: MathFunction | null): void { + this.modelValue = value; + this.mathFunctionForm.get('operation').setValue(value, {emitEvent: false}); + this.dirty = true; + } + + updateView(value: MathFunction | null) { + if (this.modelValue !== value) { + this.modelValue = value; + this.propagateChange(this.modelValue); + } + } + + onFocus() { + if (this.dirty) { + this.mathFunctionForm.get('operation').updateValueAndValidity({onlySelf: true}); + this.dirty = false; + } + } + + clear() { + this.mathFunctionForm.get('operation').patchValue(''); + setTimeout(() => { + this.operationInput.nativeElement.blur(); + this.operationInput.nativeElement.focus(); + }, 0); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/message-types-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/message-types-config.component.html new file mode 100644 index 0000000000..df52912735 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/message-types-config.component.html @@ -0,0 +1,71 @@ + + + {{ label }} + + + {{messageType.name}} + close + + + + + + + + +
    +
    + tb.rulenode.no-message-types-found +
    + + + {{ 'tb.rulenode.no-message-type-matching' | translate : + {messageType: truncate.transform(searchText, true, 6, '...')} + }} + + + + tb.rulenode.create-new-message-type + +
    +
    +
    + help + + {{ 'tb.rulenode.select-message-types-required' | translate }} + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/message-types-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/message-types-config.component.ts new file mode 100644 index 0000000000..278819cb80 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/message-types-config.component.ts @@ -0,0 +1,239 @@ +/// +/// 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, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; +import { LinkLabel, MessageType, messageTypeNames, PageComponent, TruncatePipe } from '@shared/public-api'; +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { MatChipGrid, MatChipInputEvent } from '@angular/material/chips'; +import { MatAutocomplete, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; +import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; +import { Observable, of } from 'rxjs'; +import { map, mergeMap, share, startWith } from 'rxjs/operators'; +import { TranslateService } from '@ngx-translate/core'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; + +@Component({ + selector: 'tb-message-types-config', + templateUrl: './message-types-config.component.html', + styleUrls: [], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => MessageTypesConfigComponent), + multi: true + } + ] +}) +export class MessageTypesConfigComponent extends PageComponent implements ControlValueAccessor, OnInit { + + messageTypeConfigForm: FormGroup; + + private requiredValue: boolean; + + get required(): boolean { + return this.requiredValue; + } + + @Input() + set required(value: boolean) { + this.requiredValue = coerceBooleanProperty(value); + } + + @Input() + label: string; + + @Input() + placeholder = 'tb.rulenode.add-message-type'; + + @Input() + disabled: boolean; + + @ViewChild('chipList', {static: false}) chipList: MatChipGrid; + @ViewChild('messageTypeAutocomplete', {static: false}) matAutocomplete: MatAutocomplete; + @ViewChild('messageTypeInput', {static: false}) messageTypeInput: ElementRef; + + separatorKeysCodes = [ENTER, COMMA, SEMICOLON]; + + filteredMessageTypes: Observable>; + + messageTypes: Array = []; + + private messageTypesList: Array = []; + + searchText = ''; + + private propagateChange = (v: any) => { }; + + constructor(public translate: TranslateService, + public truncate: TruncatePipe, + private fb: FormBuilder) { + super(); + this.messageTypeConfigForm = this.fb.group({ + messageType: [null] + }); + for (const type of Object.keys(MessageType)) { + this.messageTypesList.push( + { + name: messageTypeNames.get(MessageType[type]), + value: type + } + ); + } + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + ngOnInit() { + this.filteredMessageTypes = this.messageTypeConfigForm.get('messageType').valueChanges + .pipe( + startWith(''), + map((value) => value ? value : ''), + mergeMap(name => this.fetchMessageTypes(name)), + share() + ); + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.messageTypeConfigForm.disable({emitEvent: false}); + } else { + this.messageTypeConfigForm.enable({emitEvent: false}); + } + } + + writeValue(value: Array | null): void { + this.searchText = ''; + this.messageTypes.length = 0; + if (value) { + value.forEach((type: string) => { + const found = this.messageTypesList.find((messageType => messageType.value === type)); + if (found) { + this.messageTypes.push({ + name: found.name, + value: found.value + }); + } else { + this.messageTypes.push({ + name: type, + value: type + }); + } + }); + } + } + + displayMessageTypeFn(messageType?: LinkLabel): string | undefined { + return messageType ? messageType.name : undefined; + } + + textIsNotEmpty(text: string): boolean { + return text && text.length > 0; + } + + createMessageType($event: Event, value: string) { + $event.preventDefault(); + this.transformMessageType(value); + } + + add(event: MatChipInputEvent): void { + this.transformMessageType(event.value); + } + + private fetchMessageTypes(searchText?: string): Observable> { + this.searchText = searchText; + if (this.searchText && this.searchText.length) { + const search = this.searchText.toUpperCase(); + return of(this.messageTypesList.filter(messageType => messageType.name.toUpperCase().includes(search))); + } else { + return of(this.messageTypesList); + } + } + + private transformMessageType(value: string) { + if ((value || '').trim()) { + let newMessageType: LinkLabel; + const messageTypeName = value.trim(); + const existingMessageType = this.messageTypesList.find(messageType => messageType.name === messageTypeName); + if (existingMessageType) { + newMessageType = { + name: existingMessageType.name, + value: existingMessageType.value + }; + } else { + newMessageType = { + name: messageTypeName, + value: messageTypeName + }; + } + if (newMessageType) { + this.addMessageType(newMessageType); + } + } + this.clear(''); + } + + remove(messageType: LinkLabel) { + const index = this.messageTypes.indexOf(messageType); + if (index >= 0) { + this.messageTypes.splice(index, 1); + this.updateModel(); + } + } + + selected(event: MatAutocompleteSelectedEvent): void { + this.addMessageType(event.option.value); + this.clear(''); + } + + addMessageType(messageType: LinkLabel): void { + const index = this.messageTypes.findIndex(existingMessageType => existingMessageType.value === messageType.value); + if (index === -1) { + this.messageTypes.push(messageType); + this.updateModel(); + } + } + + onFocus() { + this.messageTypeConfigForm.get('messageType').updateValueAndValidity({onlySelf: true, emitEvent: true}); + } + + clear(value: string = '') { + this.messageTypeInput.nativeElement.value = value; + this.messageTypeConfigForm.get('messageType').patchValue(null, {emitEvent: true}); + setTimeout(() => { + this.messageTypeInput.nativeElement.blur(); + this.messageTypeInput.nativeElement.focus(); + }, 0); + } + + private updateModel() { + const value = this.messageTypes.map((messageType => messageType.value)); + if (this.required) { + this.chipList.errorState = !value.length; + this.propagateChange(value.length > 0 ? value : null); + } else { + this.chipList.errorState = false; + this.propagateChange(value); + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/msg-metadata-chip.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/msg-metadata-chip.component.html new file mode 100644 index 0000000000..d3fcba933c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/msg-metadata-chip.component.html @@ -0,0 +1,26 @@ + +
    +
    {{ labelText }}
    + + {{ option.name }} + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/msg-metadata-chip.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/msg-metadata-chip.component.ts new file mode 100644 index 0000000000..066cea455e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/msg-metadata-chip.component.ts @@ -0,0 +1,96 @@ +/// +/// 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, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { FetchTo, FetchToTranslation } from '../rule-node-config.models'; +import { takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; +import { TranslateService } from '@ngx-translate/core'; + +@Component({ + selector: 'tb-msg-metadata-chip', + templateUrl: './msg-metadata-chip.component.html', + providers: [{ + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => MsgMetadataChipComponent), + multi: true + }] +}) + +export class MsgMetadataChipComponent implements OnInit, ControlValueAccessor, OnDestroy { + + @Input() labelText: string; + @Input() translation: Map = FetchToTranslation; + + private propagateChange: (value: any) => void = () => {}; + private destroy$ = new Subject(); + + public chipControlGroup: FormGroup; + public selectOptions = []; + + constructor(private fb: FormBuilder, + private translate: TranslateService) {} + + ngOnInit(): void { + this.initOptions(); + this.chipControlGroup = this.fb.group({ + chipControl: [null, []] + }); + + this.chipControlGroup.get('chipControl').valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe((value) => { + if (value) { + this.propagateChange(value); + } + } + ); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + initOptions() { + for (const key of this.translation.keys()) { + this.selectOptions.push({ + value: key, + name: this.translate.instant(this.translation.get(key)) + }); + } + } + + writeValue(value: string | null): void { + this.chipControlGroup.get('chipControl').patchValue(value, {emitEvent: false}); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + if (isDisabled) { + this.chipControlGroup.disable({emitEvent: false}); + } else { + this.chipControlGroup.enable({emitEvent: false}); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/output-message-type-autocomplete.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/output-message-type-autocomplete.component.html new file mode 100644 index 0000000000..832c633d5d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/output-message-type-autocomplete.component.html @@ -0,0 +1,50 @@ + +
    + + {{'tb.rulenode.output-message-type' | translate}} + + + {{msgType.name}} + + + + + {{'tb.rulenode.message-type-value' | translate}} + + + + {{ 'tb.rulenode.message-type-value-required' | translate }} + + + {{ 'tb.rulenode.message-type-value-max-length' | translate }} + + +
    + diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/output-message-type-autocomplete.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/output-message-type-autocomplete.component.ts new file mode 100644 index 0000000000..494b686ba3 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/output-message-type-autocomplete.component.ts @@ -0,0 +1,178 @@ +/// +/// 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, forwardRef, Input, OnDestroy } from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + Validator, + Validators +} from '@angular/forms'; +import { SubscriptSizing } from '@angular/material/form-field'; +import { coerceBoolean } from '@shared/public-api'; +import { Subject, takeUntil } from 'rxjs'; + +interface MessageType { + name: string; + value: string; +} + +@Component({ + selector: 'tb-output-message-type-autocomplete', + templateUrl: './output-message-type-autocomplete.component.html', + styleUrls: [], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => OutputMessageTypeAutocompleteComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => OutputMessageTypeAutocompleteComponent), + multi: true + } + ] +}) + +export class OutputMessageTypeAutocompleteComponent implements ControlValueAccessor, Validator, OnDestroy { + + @Input() + subscriptSizing: SubscriptSizing = 'fixed'; + + @Input() + @coerceBoolean() + disabled: boolean; + + @Input() + @coerceBoolean() + set required(value) { + if (this.requiredValue !== value) { + this.requiredValue = value; + this.updateValidators(); + } + } + + get required() { + return this.requiredValue; + } + + messageTypeFormGroup: FormGroup; + + messageTypes: MessageType[] = [ + { + name: 'Post attributes', + value: 'POST_ATTRIBUTES_REQUEST' + }, + { + name: 'Post telemetry', + value: 'POST_TELEMETRY_REQUEST' + }, + { + name: 'Custom', + value: '' + }, + ]; + + private modelValue: string | null; + private requiredValue: boolean; + private propagateChange: (value: any) => void = () => {}; + private destroy$ = new Subject(); + + constructor(private fb: FormBuilder) { + this.messageTypeFormGroup = this.fb.group({ + messageTypeAlias: [null, [Validators.required]], + messageType: [{value: null, disabled: true}, [Validators.maxLength(255)]] + }); + this.messageTypeFormGroup.get('messageTypeAlias').valueChanges + .pipe(takeUntil(this.destroy$)) + .subscribe(value => this.updateMessageTypeValue(value)); + this.messageTypeFormGroup.valueChanges + .pipe(takeUntil(this.destroy$)) + .subscribe(() => this.updateView()); + } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } + + registerOnTouched(fn: any): void { + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + writeValue(value: string | null): void { + this.modelValue = value; + let findMessage = this.messageTypes.find(msgType => msgType.value === value); + if (!findMessage) { + findMessage = this.messageTypes.find(msgType => msgType.value === ''); + } + this.messageTypeFormGroup.get('messageTypeAlias').patchValue(findMessage, {emitEvent: false}); + this.messageTypeFormGroup.get('messageType').patchValue(value, {emitEvent: false}); + } + + validate() { + if (!this.messageTypeFormGroup.valid) { + return { + messageTypeInvalid: true + }; + } + return null; + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.messageTypeFormGroup.disable({emitEvent: false}); + } else { + this.messageTypeFormGroup.enable({emitEvent: false}); + if (this.messageTypeFormGroup.get('messageTypeAlias').value?.name !== 'Custom') { + this.messageTypeFormGroup.get('messageType').disable({emitEvent: false}); + } + } + } + + private updateView() { + const value = this.messageTypeFormGroup.getRawValue().messageType; + if (this.modelValue !== value) { + this.modelValue = value; + this.propagateChange(this.modelValue); + } + } + + private updateValidators() { + this.messageTypeFormGroup.get('messageType').setValidators( + this.required ? [Validators.required, Validators.maxLength(255)] : [Validators.maxLength(255)] + ); + this.messageTypeFormGroup.get('messageType').updateValueAndValidity({emitEvent: false}); + } + + private updateMessageTypeValue(choseMessageType: MessageType) { + if (choseMessageType?.name !== 'Custom') { + this.messageTypeFormGroup.get('messageType').disable({emitEvent: false}); + } else { + this.messageTypeFormGroup.get('messageType').enable({emitEvent: false}); + } + this.messageTypeFormGroup.get('messageType').patchValue(choseMessageType.value ?? null); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config-old.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config-old.component.html new file mode 100644 index 0000000000..d60e993e88 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config-old.component.html @@ -0,0 +1,45 @@ + +
    + + {{ 'alias.last-level-relation' | translate }} + +
    + + relation.direction + + + {{ directionTypeTranslations.get(type) | translate }} + + + + + tb.rulenode.max-relation-level + + +
    +
    relation.relation-filters
    + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config-old.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config-old.component.ts new file mode 100644 index 0000000000..e89cd249f8 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config-old.component.ts @@ -0,0 +1,99 @@ +/// +/// 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, forwardRef, Input, OnInit } from '@angular/core'; +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; +import { EntitySearchDirection, entitySearchDirectionTranslations, PageComponent } from '@shared/public-api'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/public-api'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { RelationsQuery } from '../rule-node-config.models'; + +@Component({ + selector: 'tb-relations-query-config-old', + templateUrl: './relations-query-config-old.component.html', + styleUrls: [], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => RelationsQueryConfigOldComponent), + multi: true + } + ] +}) +export class RelationsQueryConfigOldComponent extends PageComponent implements ControlValueAccessor, OnInit { + + @Input() disabled: boolean; + + private requiredValue: boolean; + + get required(): boolean { + return this.requiredValue; + } + + @Input() + set required(value: boolean) { + this.requiredValue = coerceBooleanProperty(value); + } + + directionTypes = Object.keys(EntitySearchDirection); + directionTypeTranslations = entitySearchDirectionTranslations; + + relationsQueryFormGroup: FormGroup; + + private propagateChange = null; + + constructor(protected store: Store, + private fb: FormBuilder) { + super(store); + } + + ngOnInit(): void { + this.relationsQueryFormGroup = this.fb.group({ + fetchLastLevelOnly: [false, []], + direction: [null, [Validators.required]], + maxLevel: [null, []], + filters: [null] + }); + this.relationsQueryFormGroup.valueChanges.subscribe((query: RelationsQuery) => { + if (this.relationsQueryFormGroup.valid) { + this.propagateChange(query); + } else { + this.propagateChange(null); + } + }); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.relationsQueryFormGroup.disable({emitEvent: false}); + } else { + this.relationsQueryFormGroup.enable({emitEvent: false}); + } + } + + writeValue(query: RelationsQuery): void { + this.relationsQueryFormGroup.reset(query || {}, {emitEvent: false}); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config.component.html new file mode 100644 index 0000000000..748076957d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config.component.html @@ -0,0 +1,61 @@ + +
    +
    tb.rulenode.relations-query
    +
    +
    + + relation.direction + + + {{ directionTypeTranslations.get(type) | translate }} tb.rulenode.relations-query-config-direction-suffix + + + + + tb.rulenode.max-relation-level + + + {{ 'tb.rulenode.max-relation-level-error' | translate }} + + + {{ 'tb.rulenode.max-relation-level-invalid' | translate }} + + +
    +
    + + {{ 'alias.last-level-relation' | translate }} + +
    +
    +
    +
    relation.relation-filters
    + + +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config.component.ts new file mode 100644 index 0000000000..d17e6f891f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config.component.ts @@ -0,0 +1,98 @@ +/// +/// 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, forwardRef, Input, OnInit } from '@angular/core'; +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; +import { EntitySearchDirection, entitySearchDirectionTranslations, PageComponent } from '@shared/public-api'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/public-api'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { RelationsQuery } from '../rule-node-config.models'; + +@Component({ + selector: 'tb-relations-query-config', + templateUrl: './relations-query-config.component.html', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => RelationsQueryConfigComponent), + multi: true + } + ] +}) +export class RelationsQueryConfigComponent extends PageComponent implements ControlValueAccessor, OnInit { + + @Input() disabled: boolean; + + private requiredValue: boolean; + + get required(): boolean { + return this.requiredValue; + } + + @Input() + set required(value: boolean) { + this.requiredValue = coerceBooleanProperty(value); + } + + directionTypes: Array = Object.values(EntitySearchDirection); + directionTypeTranslations = entitySearchDirectionTranslations; + + relationsQueryFormGroup: FormGroup; + + private propagateChange = null; + + constructor(protected store: Store, + private fb: FormBuilder) { + super(store); + } + + ngOnInit(): void { + this.relationsQueryFormGroup = this.fb.group({ + fetchLastLevelOnly: [false, []], + direction: [null, [Validators.required]], + maxLevel: [null, [Validators.min(1)]], + filters: [null] + }); + this.relationsQueryFormGroup.valueChanges.subscribe((query: RelationsQuery) => { + if (this.relationsQueryFormGroup.valid) { + this.propagateChange(query); + } else { + this.propagateChange(null); + } + }); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.relationsQueryFormGroup.disable({emitEvent: false}); + } else { + this.relationsQueryFormGroup.enable({emitEvent: false}); + } + } + + writeValue(query: RelationsQuery): void { + this.relationsQueryFormGroup.reset(query || {}, {emitEvent: false}); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/rule-node-config-common.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/rule-node-config-common.module.ts new file mode 100644 index 0000000000..5b4a1e07fa --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/rule-node-config-common.module.ts @@ -0,0 +1,80 @@ +/// +/// 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 { KvMapConfigComponent } from './kv-map-config.component'; +import { DeviceRelationsQueryConfigComponent } from './device-relations-query-config.component'; +import { RelationsQueryConfigComponent } from './relations-query-config.component'; +import { MessageTypesConfigComponent } from './message-types-config.component'; +import { CredentialsConfigComponent } from './credentials-config.component'; +import { ArgumentsMapConfigComponent } from './arguments-map-config.component'; +import { MathFunctionAutocompleteComponent } from './math-function-autocomplete.component'; +import { OutputMessageTypeAutocompleteComponent } from './output-message-type-autocomplete.component'; +import { KvMapConfigOldComponent } from './kv-map-config-old.component'; +import { MsgMetadataChipComponent } from './msg-metadata-chip.component'; +import { SvMapConfigComponent } from './sv-map-config.component'; +import { RelationsQueryConfigOldComponent } from './relations-query-config-old.component'; +import { SelectAttributesComponent } from './select-attributes.component'; +import { AlarmStatusSelectComponent } from './alarm-status-select.component'; +import { ExampleHintComponent } from './example-hint.component'; + +@NgModule({ + declarations: [ + KvMapConfigComponent, + DeviceRelationsQueryConfigComponent, + RelationsQueryConfigComponent, + MessageTypesConfigComponent, + CredentialsConfigComponent, + ArgumentsMapConfigComponent, + MathFunctionAutocompleteComponent, + OutputMessageTypeAutocompleteComponent, + KvMapConfigOldComponent, + MsgMetadataChipComponent, + SvMapConfigComponent, + RelationsQueryConfigOldComponent, + SelectAttributesComponent, + AlarmStatusSelectComponent, + ExampleHintComponent + ], + imports: [ + CommonModule, + SharedModule, + HomeComponentsModule + ], + exports: [ + KvMapConfigComponent, + DeviceRelationsQueryConfigComponent, + RelationsQueryConfigComponent, + MessageTypesConfigComponent, + CredentialsConfigComponent, + ArgumentsMapConfigComponent, + MathFunctionAutocompleteComponent, + OutputMessageTypeAutocompleteComponent, + KvMapConfigOldComponent, + MsgMetadataChipComponent, + SvMapConfigComponent, + RelationsQueryConfigOldComponent, + SelectAttributesComponent, + AlarmStatusSelectComponent, + ExampleHintComponent + ] +}) + +export class RuleNodeConfigCommonModule { +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/select-attributes.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/select-attributes.component.html new file mode 100644 index 0000000000..ab7bea5409 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/select-attributes.component.html @@ -0,0 +1,59 @@ + +
    + + + + + + + + + + + + + +
    + + {{ 'tb.rulenode.fetch-latest-telemetry-with-timestamp' | translate }} + +
    +
    + + + help + diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/select-attributes.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/select-attributes.component.ts new file mode 100644 index 0000000000..fbfbb63a14 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/select-attributes.component.ts @@ -0,0 +1,139 @@ +/// +/// 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, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + ValidatorFn, + Validators +} from '@angular/forms'; +import { takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; +import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; +import { TranslateService } from '@ngx-translate/core'; +import { isDefinedAndNotNull } from '@core/public-api'; + +@Component({ + selector: 'tb-select-attributes', + templateUrl: './select-attributes.component.html', + styleUrls: [], + providers: [{ + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => SelectAttributesComponent), + multi: true + }, { + provide: NG_VALIDATORS, + useExisting: SelectAttributesComponent, + multi: true + }] +}) + +export class SelectAttributesComponent implements OnInit, ControlValueAccessor, OnDestroy { + + private propagateChange = (v: any) => { }; + private destroy$ = new Subject(); + + public attributeControlGroup: FormGroup; + public separatorKeysCodes = [ENTER, COMMA, SEMICOLON]; + public onTouched = () => {}; + + @Input() popupHelpLink: string; + + constructor(public translate: TranslateService, + private fb: FormBuilder) { + } + + ngOnInit(): void { + this.attributeControlGroup = this.fb.group({ + clientAttributeNames: [[], []], + sharedAttributeNames: [[], []], + serverAttributeNames: [[], []], + latestTsKeyNames: [[], []], + getLatestValueWithTs: [false, []] + }, { + validators: this.atLeastOne(Validators.required, ['clientAttributeNames', 'sharedAttributeNames', + 'serverAttributeNames', 'latestTsKeyNames']) + }); + + this.attributeControlGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe((value) => { + this.propagateChange(this.preparePropagateValue(value)); + }); + } + + private preparePropagateValue(propagateValue: {[key: string]: string[] | boolean | null}): {[key: string]: string[] | boolean } { + const formatValue = {}; + for (const key in propagateValue) { + if (key === 'getLatestValueWithTs') { + formatValue[key] = propagateValue[key]; + } else { + formatValue[key] = isDefinedAndNotNull(propagateValue[key]) ? propagateValue[key] : []; + } + }; + + return formatValue; + }; + + validate() { + if (this.attributeControlGroup.valid) { + return null; + } else { + return {atLeastOneRequired: true}; + } + } + + private atLeastOne(validator: ValidatorFn, controls: string[] = null) { + return (group: FormGroup): ValidationErrors | null => { + if (!controls) { + controls = Object.keys(group.controls); + } + const hasAtLeastOne = group?.controls && controls.some(k => !validator(group.controls[k])); + + return hasAtLeastOne ? null : {atLeastOne: true}; + }; + } + + writeValue(value): void { + this.attributeControlGroup.setValue(value, {emitEvent: false}); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + this.onTouched = fn; + } + + setDisabledState(isDisabled: boolean): void { + if (isDisabled) { + this.attributeControlGroup.disable({emitEvent: false}); + } else { + this.attributeControlGroup.enable({emitEvent: false}); + } + } + + ngOnDestroy(): void { + this.destroy$.next(null); + this.destroy$.complete(); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/sv-map-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/sv-map-config.component.html new file mode 100644 index 0000000000..6deeb54f67 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/sv-map-config.component.html @@ -0,0 +1,70 @@ + +
    +
    +
    {{ labelText }}
    +
    + tb.rulenode.map-fields-required +
    +
    + {{ requiredText }} +
    +
    +
    +
    +
    +
    {{ selectText }}
    +
    {{ valText }}
    +
    +
    +
    +
    + + + + {{option.name}} + + + + + + +
    + +
    +
    +
    +
    +
    +
    + +
    + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/sv-map-config.component.scss b/ui-ngx/src/app/modules/home/components/rule-node/common/sv-map-config.component.scss new file mode 100644 index 0000000000..132a1f882c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/sv-map-config.component.scss @@ -0,0 +1,24 @@ +/** + * 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 { + .field-space { + flex: 1 1 50%; + } + + .actions-header { + width: 40px + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/sv-map-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/sv-map-config.component.ts new file mode 100644 index 0000000000..df9d3f0b3d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/sv-map-config.component.ts @@ -0,0 +1,266 @@ +/// +/// 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, forwardRef, Injector, Input, OnDestroy, OnInit } from '@angular/core'; +import { + AbstractControl, + ControlValueAccessor, + FormArray, + FormBuilder, + FormControl, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + NgControl, + ValidationErrors, + Validator, + ValidatorFn, + Validators +} from '@angular/forms'; +import { coerceBoolean, PageComponent } from '@shared/public-api'; +import { Store } from '@ngrx/store'; +import { AppState, isDefinedAndNotNull, isEqual } from '@core/public-api'; +import { Subject, Subscription } from 'rxjs'; +import { TranslateService } from '@ngx-translate/core'; +import { takeUntil } from 'rxjs/operators'; +import { OriginatorFieldsMappingValues, SvMapOption } from '../rule-node-config.models'; + +@Component({ + selector: 'tb-sv-map-config', + templateUrl: './sv-map-config.component.html', + styleUrls: ['./sv-map-config.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => SvMapConfigComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => SvMapConfigComponent), + multi: true, + } + ] +}) +export class SvMapConfigComponent extends PageComponent implements ControlValueAccessor, OnInit, Validator, OnDestroy { + + private destroy$ = new Subject(); + private sourceFieldSubcritption: Subscription[] = []; + private propagateChange = null; + + svListFormGroup: FormGroup; + ngControl: NgControl; + + @Input() selectOptions: SvMapOption[]; + + @Input() + @coerceBoolean() + disabled = false; + + @Input() labelText: string; + + @Input() requiredText: string; + + @Input() targetKeyPrefix: string; + + @Input() selectText: string; + + @Input() selectRequiredText: string; + + @Input() valText: string; + + @Input() valRequiredText: string; + + @Input() hintText: string; + + @Input() popupHelpLink: string; + + @Input() + @coerceBoolean() + required = false; + + constructor(protected store: Store, + public translate: TranslateService, + public injector: Injector, + private fb: FormBuilder) { + super(store); + } + + ngOnInit(): void { + this.ngControl = this.injector.get(NgControl); + if (this.ngControl != null) { + this.ngControl.valueAccessor = this; + } + + this.svListFormGroup = this.fb.group({ + keyVals: this.fb.array([]) + }, {validators: [this.propagateNestedErrors, this.oneMapRequiredValidator]}); + + this.svListFormGroup.valueChanges + .pipe(takeUntil(this.destroy$)) + .subscribe(() => { + this.updateModel(); + }); + } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } + + keyValsFormArray(): FormArray { + return this.svListFormGroup.get('keyVals') as FormArray; + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.svListFormGroup.disable({emitEvent: false}); + } else { + this.svListFormGroup.enable({emitEvent: false}); + } + } + + private oneMapRequiredValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => control.get('keyVals').value.length; + + private propagateNestedErrors: ValidatorFn = (controls: FormArray | FormGroup | AbstractControl): ValidationErrors | null => { + if (this.svListFormGroup && this.svListFormGroup.get('keyVals') && this.svListFormGroup.get('keyVals')?.status === 'VALID') { + return null; + } + const errors = {}; + if (this.svListFormGroup) {this.svListFormGroup.setErrors(null);} + if (controls instanceof FormArray || controls instanceof FormGroup) { + if (controls.errors) { + for (const errorKey of Object.keys(controls.errors)) { + errors[errorKey] = true; + } + } + for (const control of Object.keys(controls.controls)) { + const innerErrors = this.propagateNestedErrors(controls.controls[control]); + if (innerErrors && Object.keys(innerErrors).length) { + for (const errorKey of Object.keys(innerErrors)) { + errors[errorKey] = true; + } + } + } + return errors; + } else { + if (controls.errors) { + for (const errorKey of Object.keys(controls.errors)) { + errors[errorKey] = true; + } + } + } + return !isEqual(errors, {}) ? errors : null; + }; + + writeValue(keyValMap: { [key: string]: string }): void { + const keyValuesData = Object.keys(keyValMap).map(key => ({key, value: keyValMap[key]})); + if (this.keyValsFormArray().length === keyValuesData.length) { + this.keyValsFormArray().patchValue(keyValuesData, {emitEvent: false}) + } else { + const keyValsControls: Array = []; + keyValuesData.forEach(data => { + keyValsControls.push(this.fb.group({ + key: [data.key, [Validators.required, ]], + value: [data.value, [Validators.required, Validators.pattern(/(?:.|\s)*\S(&:.|\s)*/)]] + })); + }); + this.svListFormGroup.setControl('keyVals', this.fb.array(keyValsControls, this.propagateNestedErrors), {emitEvent: false}); + for (const formGroup of this.keyValsFormArray().controls) { + this.keyChangeSubscribe(formGroup as FormGroup); + } + } + } + + public filterSelectOptions(keyValControl?: AbstractControl) { + const deleteFieldsArray = []; + for (const fieldMap of this.svListFormGroup.get('keyVals').value) { + const findDeleteField = this.selectOptions.find((field) => field.value === fieldMap.key); + if (findDeleteField) { + deleteFieldsArray.push(findDeleteField); + } + } + + const filterSelectOptions = []; + for (const selectOption of this.selectOptions) { + if (!isDefinedAndNotNull(deleteFieldsArray.find((deleteField) => deleteField.value === selectOption.value)) || + selectOption.value === keyValControl?.get('key').value) { + filterSelectOptions.push(selectOption); + } + } + + return filterSelectOptions; + } + + public removeKeyVal(index: number) { + this.keyValsFormArray().removeAt(index); + this.sourceFieldSubcritption[index].unsubscribe(); + this.sourceFieldSubcritption.splice(index, 1); + } + + public addKeyVal() { + this.keyValsFormArray().push(this.fb.group({ + key: ['', [Validators.required]], + value: ['', [Validators.required, Validators.pattern(/(?:.|\s)*\S(&:.|\s)*/)]] + })); + this.keyChangeSubscribe(this.keyValsFormArray().at(this.keyValsFormArray().length - 1) as FormGroup); + } + + private keyChangeSubscribe(formGroup: FormGroup) { + this.sourceFieldSubcritption.push(formGroup.get('key').valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe((value) => { + const mappedValue = OriginatorFieldsMappingValues.get(value); + formGroup.get('value').patchValue(this.targetKeyPrefix + mappedValue[0].toUpperCase() + mappedValue.slice(1)); + })); + } + + public validate(c: FormControl) { + const svList: { key: string; value: string }[] = this.svListFormGroup.get('keyVals').value; + if (!svList.length && this.required) { + return { + svMapRequired: true + }; + } + if (!this.svListFormGroup.valid) { + return { + svFieldsRequired: true + }; + } + return null; + } + + private updateModel() { + const svList: { key: string; value: string }[] = this.svListFormGroup.get('keyVals').value; + if (this.required && !svList.length || !this.svListFormGroup.valid) { + this.propagateChange(null); + } else { + const keyValMap: { [key: string]: string } = {}; + svList.forEach((entry) => { + keyValMap[entry.key] = entry.value; + }); + this.propagateChange(keyValMap); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/empty-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/empty-config.component.ts new file mode 100644 index 0000000000..48f350157f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/empty-config.component.ts @@ -0,0 +1,42 @@ +/// +/// 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-node-empty-config', + template: '
    ', + styleUrls: [] +}) +export class EmptyConfigComponent extends RuleNodeConfigurationComponent { + + emptyConfigForm: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.emptyConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.emptyConfigForm = this.fb.group({}); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/calculate-delta-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/calculate-delta-config.component.html new file mode 100644 index 0000000000..b727008bb3 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/calculate-delta-config.component.html @@ -0,0 +1,92 @@ + +
    +
    + + {{ 'tb.rulenode.input-value-key' | translate }} + + + {{ 'tb.rulenode.input-value-key-required' | translate }} + + + + {{ 'tb.rulenode.output-value-key' | translate }} + + + {{ 'tb.rulenode.output-value-key-required' | translate }} + + +
    + + {{ 'tb.rulenode.number-of-digits-after-floating-point' | translate }} + + + {{ 'tb.rulenode.number-of-digits-after-floating-point-range' | translate }} + + + {{ 'tb.rulenode.number-of-digits-after-floating-point-range' | translate }} + + +
    +
    + + {{ 'tb.rulenode.failure-if-delta-negative' | translate }} + +
    +
    + + {{ 'tb.rulenode.use-caching' | translate }} + +
    +
    +
    + + {{ 'tb.rulenode.add-time-difference-between-readings' | translate: + { inputValueKey: calculateDeltaConfigForm.get('inputValueKey').valid ? + calculateDeltaConfigForm.get('inputValueKey').value : 'tb.rulenode.input-value-key' | translate } }} + +
    + + {{ 'tb.rulenode.period-value-key' | translate }} + + + {{ 'tb.rulenode.period-value-key-required' | translate }} + + +
    +
    + + {{ 'tb.rulenode.exclude-zero-deltas' | translate }} + +
    +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/calculate-delta-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/calculate-delta-config.component.ts new file mode 100644 index 0000000000..1721b5dfb0 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/calculate-delta-config.component.ts @@ -0,0 +1,87 @@ +/// +/// 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 { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; +import { TranslateService } from '@ngx-translate/core'; +import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; +import { deepTrim, isDefinedAndNotNull } from '@app/core/utils'; + +@Component({ + selector: 'tb-enrichment-node-calculate-delta-config', + templateUrl: './calculate-delta-config.component.html' +}) +export class CalculateDeltaConfigComponent extends RuleNodeConfigurationComponent { + + calculateDeltaConfigForm: FormGroup; + + separatorKeysCodes = [ENTER, COMMA, SEMICOLON]; + + constructor(public translate: TranslateService, + private fb: FormBuilder) { + super(); + } + + protected configForm(): FormGroup { + return this.calculateDeltaConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.calculateDeltaConfigForm = this.fb.group({ + inputValueKey: [configuration.inputValueKey, [Validators.required, Validators.pattern(/(?:.|\s)*\S(&:.|\s)*/)]], + outputValueKey: [configuration.outputValueKey, [Validators.required, Validators.pattern(/(?:.|\s)*\S(&:.|\s)*/)]], + useCache: [configuration.useCache, []], + addPeriodBetweenMsgs: [configuration.addPeriodBetweenMsgs, []], + periodValueKey: [configuration.periodValueKey, []], + round: [configuration.round, [Validators.min(0), Validators.max(15)]], + tellFailureIfDeltaIsNegative: [configuration.tellFailureIfDeltaIsNegative, []], + excludeZeroDeltas: [configuration.excludeZeroDeltas, []] + }); + } + + protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + return { + inputValueKey: isDefinedAndNotNull(configuration?.inputValueKey) ? configuration.inputValueKey : null, + outputValueKey: isDefinedAndNotNull(configuration?.outputValueKey) ? configuration.outputValueKey : null, + useCache: isDefinedAndNotNull(configuration?.useCache) ? configuration.useCache : true, + addPeriodBetweenMsgs: isDefinedAndNotNull(configuration?.addPeriodBetweenMsgs) ? configuration.addPeriodBetweenMsgs : false, + periodValueKey: isDefinedAndNotNull(configuration?.periodValueKey) ? configuration.periodValueKey : null, + round: isDefinedAndNotNull(configuration?.round) ? configuration.round : null, + tellFailureIfDeltaIsNegative: isDefinedAndNotNull(configuration?.tellFailureIfDeltaIsNegative) ? + configuration.tellFailureIfDeltaIsNegative : true, + excludeZeroDeltas: isDefinedAndNotNull(configuration?.excludeZeroDeltas) ? configuration.excludeZeroDeltas : false + }; + } + + protected prepareOutputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + return deepTrim(configuration); + } + + protected updateValidators(emitEvent: boolean) { + const addPeriodBetweenMsgs: boolean = this.calculateDeltaConfigForm.get('addPeriodBetweenMsgs').value; + if (addPeriodBetweenMsgs) { + this.calculateDeltaConfigForm.get('periodValueKey').setValidators([Validators.required]); + } else { + this.calculateDeltaConfigForm.get('periodValueKey').setValidators([]); + } + this.calculateDeltaConfigForm.get('periodValueKey').updateValueAndValidity({emitEvent}); + } + + protected validatorTriggers(): string[] { + return ['addPeriodBetweenMsgs']; + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/customer-attributes-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/customer-attributes-config.component.html new file mode 100644 index 0000000000..182d580eec --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/customer-attributes-config.component.html @@ -0,0 +1,46 @@ + +
    +
    tb.rulenode.mapping-of-customers
    +
    +
    + + + {{ data.name }} + + +
    +
    + + + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/customer-attributes-config.component.scss b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/customer-attributes-config.component.scss new file mode 100644 index 0000000000..aea1da60f3 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/customer-attributes-config.component.scss @@ -0,0 +1,23 @@ +/** + * 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 { + .fetch-to-data-toggle { + max-width: 420px; + width: 100%; + } +} + + diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/customer-attributes-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/customer-attributes-config.component.ts new file mode 100644 index 0000000000..3eabe0ab4f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/customer-attributes-config.component.ts @@ -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. +/// + +import { Component } from '@angular/core'; +import { deepTrim, isDefinedAndNotNull } from '@core/public-api'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { TranslateService } from '@ngx-translate/core'; +import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models'; +import { DataToFetch, dataToFetchTranslations, FetchTo } from '@home/components/rule-node/rule-node-config.models'; + +@Component({ + selector: 'tb-enrichment-node-customer-attributes-config', + templateUrl: './customer-attributes-config.component.html', + styleUrls: ['./customer-attributes-config.component.scss'] +}) +export class CustomerAttributesConfigComponent extends RuleNodeConfigurationComponent { + + customerAttributesConfigForm: FormGroup; + + public fetchToData = []; + + constructor(private fb: FormBuilder, + private translate: TranslateService) { + super(); + for (const key of dataToFetchTranslations.keys()) { + if (key !== DataToFetch.FIELDS) { + this.fetchToData.push({ + value: key, + name: this.translate.instant(dataToFetchTranslations.get(key as DataToFetch)) + }); + } + } + } + + protected configForm(): FormGroup { + return this.customerAttributesConfigForm; + } + + protected prepareOutputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + const filteDataMapping = {}; + for (const key of Object.keys(configuration.dataMapping)) { + filteDataMapping[key.trim()] = configuration.dataMapping[key]; + } + configuration.dataMapping = filteDataMapping; + return deepTrim(configuration); + } + + protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + let dataToFetch: DataToFetch; + if (isDefinedAndNotNull(configuration?.telemetry)) { + dataToFetch = configuration.telemetry ? DataToFetch.LATEST_TELEMETRY : DataToFetch.ATTRIBUTES; + } else { + dataToFetch = isDefinedAndNotNull(configuration?.dataToFetch) ? configuration.dataToFetch : DataToFetch.ATTRIBUTES; + } + + let dataMapping; + if (isDefinedAndNotNull(configuration?.attrMapping)) { + dataMapping = configuration.attrMapping; + } else { + dataMapping = isDefinedAndNotNull(configuration?.dataMapping) ? configuration.dataMapping : null; + } + + return { + dataToFetch, + dataMapping, + fetchTo: isDefinedAndNotNull(configuration?.fetchTo) ? configuration.fetchTo : FetchTo.METADATA + }; + } + + public selectTranslation(latestTelemetryTranslation: string, attributesTranslation: string) { + if (this.customerAttributesConfigForm.get('dataToFetch').value === DataToFetch.LATEST_TELEMETRY) { + return latestTelemetryTranslation; + } else { + return attributesTranslation; + } + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.customerAttributesConfigForm = this.fb.group({ + dataToFetch: [configuration.dataToFetch, []], + dataMapping: [configuration.dataMapping, [Validators.required]], + fetchTo: [configuration.fetchTo] + }); + } + + protected readonly DataToFetch = DataToFetch; +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/device-attributes-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/device-attributes-config.component.html new file mode 100644 index 0000000000..c17edb1e92 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/device-attributes-config.component.html @@ -0,0 +1,46 @@ + +
    +
    +
    tb.rulenode.device-relations-query
    + + +
    +
    +
    +
    tb.rulenode.related-device-attributes
    +
    + tb.rulenode.at-least-one-field-required +
    +
    + + +
    +
    + + {{ 'tb.rulenode.tell-failure' | translate }} + +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/device-attributes-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/device-attributes-config.component.ts new file mode 100644 index 0000000000..876e672b13 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/device-attributes-config.component.ts @@ -0,0 +1,77 @@ +/// +/// 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, isObject } from '@core/public-api'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { TranslateService } from '@ngx-translate/core'; +import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models'; +import { FetchTo } from '@home/components/rule-node/rule-node-config.models'; + +@Component({ + selector: 'tb-enrichment-node-device-attributes-config', + templateUrl: './device-attributes-config.component.html', + styleUrls: [] +}) +export class DeviceAttributesConfigComponent extends RuleNodeConfigurationComponent { + + deviceAttributesConfigForm: FormGroup; + + constructor(public translate: TranslateService, + private fb: FormBuilder) { + super(); + } + + protected configForm(): FormGroup { + return this.deviceAttributesConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.deviceAttributesConfigForm = this.fb.group({ + deviceRelationsQuery: [configuration.deviceRelationsQuery, [Validators.required]], + tellFailureIfAbsent: [configuration.tellFailureIfAbsent, []], + fetchTo: [configuration.fetchTo, []], + attributesControl: [configuration.attributesControl, []] + }); + } + + protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + if (isObject(configuration)) { + configuration.attributesControl = { + clientAttributeNames: isDefinedAndNotNull(configuration?.clientAttributeNames) ? configuration.clientAttributeNames : [], + latestTsKeyNames: isDefinedAndNotNull(configuration?.latestTsKeyNames) ? configuration.latestTsKeyNames : [], + serverAttributeNames: isDefinedAndNotNull(configuration?.serverAttributeNames) ? configuration.serverAttributeNames : [], + sharedAttributeNames: isDefinedAndNotNull(configuration?.sharedAttributeNames) ? configuration.sharedAttributeNames : [], + getLatestValueWithTs: isDefinedAndNotNull(configuration?.getLatestValueWithTs) ? configuration.getLatestValueWithTs : false, + }; + } + + return { + deviceRelationsQuery: isDefinedAndNotNull(configuration?.deviceRelationsQuery) ? configuration.deviceRelationsQuery : null, + tellFailureIfAbsent: isDefinedAndNotNull(configuration?.tellFailureIfAbsent) ? configuration.tellFailureIfAbsent : true, + fetchTo: isDefinedAndNotNull(configuration?.fetchTo) ? configuration.fetchTo : FetchTo.METADATA, + attributesControl: configuration ? configuration.attributesControl : null + }; + } + + protected prepareOutputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + for (const key of Object.keys(configuration.attributesControl)) { + configuration[key] = configuration.attributesControl[key]; + } + delete configuration.attributesControl; + return configuration; + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/entity-details-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/entity-details-config.component.html new file mode 100644 index 0000000000..1065aa6a09 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/entity-details-config.component.html @@ -0,0 +1,35 @@ + +
    + + + help + + + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/entity-details-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/entity-details-config.component.ts new file mode 100644 index 0000000000..808eaf35b2 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/entity-details-config.component.ts @@ -0,0 +1,87 @@ +/// +/// 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, OnInit } from '@angular/core'; +import { isDefinedAndNotNull } from '@core/public-api'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { TranslateService } from '@ngx-translate/core'; +import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models'; +import { + EntityDetailsField, + entityDetailsTranslations, + FetchTo +} from '@home/components/rule-node/rule-node-config.models'; + +@Component({ + selector: 'tb-enrichment-node-entity-details-config', + templateUrl: './entity-details-config.component.html', + styleUrls: [] +}) + +export class EntityDetailsConfigComponent extends RuleNodeConfigurationComponent implements OnInit { + + entityDetailsConfigForm: FormGroup; + + public predefinedValues = []; + + constructor(public translate: TranslateService, + private fb: FormBuilder) { + super(); + for (const field of Object.keys(EntityDetailsField)) { + this.predefinedValues.push({ + value: EntityDetailsField[field], + name: this.translate.instant(entityDetailsTranslations.get(EntityDetailsField[field])) + }); + } + } + + ngOnInit() { + super.ngOnInit(); + } + + protected configForm(): FormGroup { + return this.entityDetailsConfigForm; + } + + protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + let fetchTo: FetchTo; + if (isDefinedAndNotNull(configuration?.addToMetadata)) { + if (configuration.addToMetadata) { + fetchTo = FetchTo.METADATA; + } else { + fetchTo = FetchTo.DATA; + } + } else { + if (configuration?.fetchTo) { + fetchTo = configuration.fetchTo; + } else { + fetchTo = FetchTo.DATA; + } + } + + return { + detailsList: isDefinedAndNotNull(configuration?.detailsList) ? configuration.detailsList : null, + fetchTo + }; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.entityDetailsConfigForm = this.fb.group({ + detailsList: [configuration.detailsList, [Validators.required]], + fetchTo: [configuration.fetchTo, []] + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/fetch-device-credentials-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/fetch-device-credentials-config.component.html new file mode 100644 index 0000000000..a76d3341d7 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/fetch-device-credentials-config.component.html @@ -0,0 +1,23 @@ + +
    + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/fetch-device-credentials-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/fetch-device-credentials-config.component.ts new file mode 100644 index 0000000000..1b16674566 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/fetch-device-credentials-config.component.ts @@ -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. +/// + +import { Component } from '@angular/core'; +import { isDefinedAndNotNull } from '@core/public-api'; +import { FormBuilder, FormGroup } from '@angular/forms'; +import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; +import { FetchTo } from '@home/components/rule-node/rule-node-config.models'; + +@Component({ + selector: 'tb-enrichment-node-fetch-device-credentials-config', + templateUrl: './fetch-device-credentials-config.component.html' +}) + +export class FetchDeviceCredentialsConfigComponent extends RuleNodeConfigurationComponent { + + fetchDeviceCredentialsConfigForm: FormGroup; + + constructor(private fb: FormBuilder) { + super(); + } + + protected configForm(): FormGroup { + return this.fetchDeviceCredentialsConfigForm; + } + + protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + return { + fetchTo: isDefinedAndNotNull(configuration?.fetchTo) ? configuration.fetchTo : FetchTo.METADATA + }; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.fetchDeviceCredentialsConfigForm = this.fb.group({ + fetchTo: [configuration.fetchTo, []] + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/get-telemetry-from-database-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/get-telemetry-from-database-config.component.html new file mode 100644 index 0000000000..450ba10d95 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/get-telemetry-from-database-config.component.html @@ -0,0 +1,182 @@ + +
    + +
    + help + +
    +
    +
    tb.rulenode.fetch-interval
    +
    + + {{ 'tb.rulenode.use-metadata-dynamic-interval' | translate }} + +
    +
    +
    + + {{ 'tb.rulenode.interval-start' | translate }} + + + {{ 'tb.rulenode.start-interval-value-required' | translate }} + + + {{ 'tb.rulenode.time-value-range' | translate }} + + + {{ 'tb.rulenode.time-value-range' | translate }} + + + + {{ 'tb.rulenode.time-unit' | translate }} + + + {{ timeUnitsTranslationMap.get(timeUnit) | translate }} + + + +
    +
    + + {{ 'tb.rulenode.interval-end' | translate }} + + + {{ 'tb.rulenode.end-interval-value-required' | translate }} + + + {{ 'tb.rulenode.time-value-range' | translate }} + + + {{ 'tb.rulenode.time-value-range' | translate }} + + + + {{ 'tb.rulenode.time-unit' | translate }} + + + {{ timeUnitsTranslationMap.get(timeUnit) | translate }} + + + +
    +
    + error_outline +
    + + {{ 'tb.rulenode.fetch-timeseries-from-to' | translate: + { + startInterval: getTelemetryFromDatabaseConfigForm.get('interval.startInterval').value, + endInterval: getTelemetryFromDatabaseConfigForm.get('interval.endInterval').value, + startIntervalTimeUnit: getTelemetryFromDatabaseConfigForm.get('interval.startIntervalTimeUnit').value.toLowerCase(), + endIntervalTimeUnit: getTelemetryFromDatabaseConfigForm.get('interval.endIntervalTimeUnit').value.toLowerCase() + } }} + + + {{ "tb.rulenode.fetch-timeseries-from-to-invalid" | translate }} + +
    +
    +
    + +
    + + {{ 'tb.rulenode.start-interval' | translate }} + + + {{ 'tb.rulenode.start-interval-required' | translate }} + + + + {{ 'tb.rulenode.end-interval' | translate }} + + + {{ 'tb.rulenode.end-interval-required' | translate }} + + + + +
    +
    +
    +
    +
    tb.rulenode.fetch-strategy
    +
    +
    + + + {{ data.name }} + + +
    +
    + {{ deduplicationStrategiesHintTranslations.get(getTelemetryFromDatabaseConfigForm.get('fetchMode').value) | translate }} +
    +
    +
    + + {{ 'aggregation.function' | translate }} + + + {{ aggregationTypesTranslations.get(aggregationTypes[aggregation]) | translate }} + + + +
    + + {{ "tb.rulenode.order-by-timestamp" | translate }} + + + {{ samplingOrdersTranslate.get(order) | translate }} + + + + + {{ "tb.rulenode.limit" | translate }} + + {{ "tb.rulenode.limit-hint" | translate }} + + {{ 'tb.rulenode.limit-required' | translate }} + + + {{ 'tb.rulenode.limit-range' | translate }} + + + {{ 'tb.rulenode.limit-range' | translate }} + + +
    +
    +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/get-telemetry-from-database-config.component.scss b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/get-telemetry-from-database-config.component.scss new file mode 100644 index 0000000000..f1fe8725e3 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/get-telemetry-from-database-config.component.scss @@ -0,0 +1,66 @@ +/** + * 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 { + + .see-example { + display: inline-block; + } + + .description-block { + display: flex; + align-items: center; + border-radius: 6px; + border: 1px solid #EAEAEA; + + .description-icon { + font-size: 24px; + height: 24px; + min-height: 24px; + width: 24px; + min-width: 24px; + line-height: 24px; + color: #D9D9D9; + margin: 4px; + } + + .description-text { + font-size: 12px; + line-height: 16px; + letter-spacing: 0.25px; + margin: 6px; + } + + &.error { + color: var(--mdc-theme-error, #f44336); + + .description-icon { + color: var(--mdc-theme-error, #f44336); + } + } + } + .item-center { + align-items: center; + + .fetch-mod-toggle { + width: 100%; + } + } + + .hint-container { + width: 100%; + } +} + diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/get-telemetry-from-database-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/get-telemetry-from-database-config.component.ts new file mode 100644 index 0000000000..13aeae87ed --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/get-telemetry-from-database-config.component.ts @@ -0,0 +1,205 @@ +/// +/// 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 { deepTrim, isDefinedAndNotNull, isObject } from '@core/public-api'; +import { AbstractControl, FormBuilder, FormGroup, ValidationErrors, Validators } from '@angular/forms'; +import { TranslateService } from '@ngx-translate/core'; +import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models'; +import { aggregationTranslations, AggregationType } from '@app/shared/models/time/time.models'; +import { + deduplicationStrategiesHintTranslations, + deduplicationStrategiesTranslations, + FetchMode, + SamplingOrder, + samplingOrderTranslations, + TimeUnit, + timeUnitTranslations +} from '../rule-node-config.models'; + +@Component({ + selector: 'tb-enrichment-node-get-telemetry-from-database', + templateUrl: './get-telemetry-from-database-config.component.html', + styleUrls: ['./get-telemetry-from-database-config.component.scss'] +}) +export class GetTelemetryFromDatabaseConfigComponent extends RuleNodeConfigurationComponent { + + getTelemetryFromDatabaseConfigForm: FormGroup; + + aggregationTypes = AggregationType; + aggregations: Array = Object.values(AggregationType); + aggregationTypesTranslations = aggregationTranslations; + + fetchMode = FetchMode; + + samplingOrders: Array = Object.values(SamplingOrder); + samplingOrdersTranslate = samplingOrderTranslations; + + timeUnits: Array = Object.values(TimeUnit); + timeUnitsTranslationMap = timeUnitTranslations; + + public deduplicationStrategiesHintTranslations = deduplicationStrategiesHintTranslations; + + headerOptions = []; + + + timeUnitMap = { + [TimeUnit.MILLISECONDS]: 1, + [TimeUnit.SECONDS]: 1000, + [TimeUnit.MINUTES]: 60000, + [TimeUnit.HOURS]: 3600000, + [TimeUnit.DAYS]: 86400000, + }; + + constructor(public translate: TranslateService, + private fb: FormBuilder) { + super(); + for (const key of deduplicationStrategiesTranslations.keys()) { + this.headerOptions.push({ + value: key, + name: this.translate.instant(deduplicationStrategiesTranslations.get(key)) + }); + } + } + + protected configForm(): FormGroup { + return this.getTelemetryFromDatabaseConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.getTelemetryFromDatabaseConfigForm = this.fb.group({ + latestTsKeyNames: [configuration.latestTsKeyNames, [Validators.required]], + aggregation: [configuration.aggregation, [Validators.required]], + fetchMode: [configuration.fetchMode, [Validators.required]], + orderBy: [configuration.orderBy, []], + limit: [configuration.limit, []], + useMetadataIntervalPatterns: [configuration.useMetadataIntervalPatterns, []], + interval: this.fb.group({ + startInterval: [configuration.interval.startInterval, []], + startIntervalTimeUnit: [configuration.interval.startIntervalTimeUnit, []], + endInterval: [configuration.interval.endInterval, []], + endIntervalTimeUnit: [configuration.interval.endIntervalTimeUnit, []], + }), + startIntervalPattern: [configuration.startIntervalPattern, []], + endIntervalPattern: [configuration.endIntervalPattern, []], + }); + } + + + private intervalValidator = () => (control: AbstractControl): ValidationErrors | null => { + if (control.get('startInterval').value * this.timeUnitMap[control.get('startIntervalTimeUnit').value] <= + control.get('endInterval').value * this.timeUnitMap[control.get('endIntervalTimeUnit').value]) { + return {intervalError: true}; + } else { + return null; + } + }; + + + protected validatorTriggers(): string[] { + return ['fetchMode', 'useMetadataIntervalPatterns']; + } + + protected prepareOutputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + configuration.startInterval = configuration.interval.startInterval; + configuration.startIntervalTimeUnit = configuration.interval.startIntervalTimeUnit; + configuration.endInterval = configuration.interval.endInterval; + configuration.endIntervalTimeUnit = configuration.interval.endIntervalTimeUnit; + delete configuration.interval; + return deepTrim(configuration); + } + + protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + if (isObject(configuration)) { + configuration.interval = { + startInterval: configuration.startInterval, + startIntervalTimeUnit: configuration.startIntervalTimeUnit, + endInterval: configuration.endInterval, + endIntervalTimeUnit: configuration.endIntervalTimeUnit + }; + } + + return { + latestTsKeyNames: isDefinedAndNotNull(configuration?.latestTsKeyNames) ? configuration.latestTsKeyNames : null, + aggregation: isDefinedAndNotNull(configuration?.aggregation) ? configuration.aggregation : AggregationType.NONE, + fetchMode: isDefinedAndNotNull(configuration?.fetchMode) ? configuration.fetchMode : FetchMode.FIRST, + orderBy: isDefinedAndNotNull(configuration?.orderBy) ? configuration.orderBy : SamplingOrder.ASC, + limit: isDefinedAndNotNull(configuration?.limit) ? configuration.limit : 1000, + useMetadataIntervalPatterns: isDefinedAndNotNull(configuration?.useMetadataIntervalPatterns) ? + configuration.useMetadataIntervalPatterns : false, + interval: { + startInterval: isDefinedAndNotNull(configuration?.interval?.startInterval) ? configuration.interval.startInterval : 2, + startIntervalTimeUnit: isDefinedAndNotNull(configuration?.interval?.startIntervalTimeUnit) ? + configuration.interval.startIntervalTimeUnit : TimeUnit.MINUTES, + endInterval: isDefinedAndNotNull(configuration?.interval?.endInterval) ? configuration.interval.endInterval : 1, + endIntervalTimeUnit: isDefinedAndNotNull(configuration?.interval?.endIntervalTimeUnit) ? + configuration.interval.endIntervalTimeUnit : TimeUnit.MINUTES, + }, + startIntervalPattern: isDefinedAndNotNull(configuration?.startIntervalPattern) ? configuration.startIntervalPattern : null, + endIntervalPattern: isDefinedAndNotNull(configuration?.endIntervalPattern) ? configuration.endIntervalPattern : null + }; + } + + protected updateValidators(emitEvent: boolean) { + const fetchMode: FetchMode = this.getTelemetryFromDatabaseConfigForm.get('fetchMode').value; + const useMetadataIntervalPatterns: boolean = this.getTelemetryFromDatabaseConfigForm.get('useMetadataIntervalPatterns').value; + if (fetchMode && fetchMode === FetchMode.ALL) { + this.getTelemetryFromDatabaseConfigForm.get('aggregation').setValidators([Validators.required]); + this.getTelemetryFromDatabaseConfigForm.get('orderBy').setValidators([Validators.required]); + this.getTelemetryFromDatabaseConfigForm.get('limit').setValidators([Validators.required, Validators.min(2), Validators.max(1000)]); + } else { + this.getTelemetryFromDatabaseConfigForm.get('aggregation').setValidators([]); + this.getTelemetryFromDatabaseConfigForm.get('orderBy').setValidators([]); + this.getTelemetryFromDatabaseConfigForm.get('limit').setValidators([]); + } + if (useMetadataIntervalPatterns) { + this.getTelemetryFromDatabaseConfigForm.get('interval.startInterval').setValidators([]); + this.getTelemetryFromDatabaseConfigForm.get('interval.startIntervalTimeUnit').setValidators([]); + this.getTelemetryFromDatabaseConfigForm.get('interval.endInterval').setValidators([]); + this.getTelemetryFromDatabaseConfigForm.get('interval.endIntervalTimeUnit').setValidators([]); + this.getTelemetryFromDatabaseConfigForm.get('interval').setValidators([]); + this.getTelemetryFromDatabaseConfigForm.get('startIntervalPattern').setValidators([Validators.required, + Validators.pattern(/(?:.|\s)*\S(&:.|\s)*/)]); + this.getTelemetryFromDatabaseConfigForm.get('endIntervalPattern').setValidators([Validators.required, + Validators.pattern(/(?:.|\s)*\S(&:.|\s)*/)]); + } else { + this.getTelemetryFromDatabaseConfigForm.get('interval.startInterval').setValidators([Validators.required, + Validators.min(1), Validators.max(2147483647)]); + this.getTelemetryFromDatabaseConfigForm.get('interval.startIntervalTimeUnit').setValidators([Validators.required]); + this.getTelemetryFromDatabaseConfigForm.get('interval.endInterval').setValidators([Validators.required, + Validators.min(1), Validators.max(2147483647)]); + this.getTelemetryFromDatabaseConfigForm.get('interval.endIntervalTimeUnit').setValidators([Validators.required]); + this.getTelemetryFromDatabaseConfigForm.get('interval').setValidators([this.intervalValidator()]); + this.getTelemetryFromDatabaseConfigForm.get('startIntervalPattern').setValidators([]); + this.getTelemetryFromDatabaseConfigForm.get('endIntervalPattern').setValidators([]); + } + this.getTelemetryFromDatabaseConfigForm.get('aggregation').updateValueAndValidity({emitEvent}); + this.getTelemetryFromDatabaseConfigForm.get('orderBy').updateValueAndValidity({emitEvent}); + this.getTelemetryFromDatabaseConfigForm.get('limit').updateValueAndValidity({emitEvent}); + this.getTelemetryFromDatabaseConfigForm.get('interval.startInterval').updateValueAndValidity({emitEvent}); + this.getTelemetryFromDatabaseConfigForm.get('interval.startIntervalTimeUnit').updateValueAndValidity({emitEvent}); + this.getTelemetryFromDatabaseConfigForm.get('interval.endInterval').updateValueAndValidity({emitEvent}); + this.getTelemetryFromDatabaseConfigForm.get('interval.endIntervalTimeUnit').updateValueAndValidity({emitEvent}); + this.getTelemetryFromDatabaseConfigForm.get('interval').updateValueAndValidity({emitEvent}); + this.getTelemetryFromDatabaseConfigForm.get('startIntervalPattern').updateValueAndValidity({emitEvent}); + this.getTelemetryFromDatabaseConfigForm.get('endIntervalPattern').updateValueAndValidity({emitEvent}); + } + + public defaultPaddingEnable() { + return this.getTelemetryFromDatabaseConfigForm.get('fetchMode').value === FetchMode.ALL && + this.getTelemetryFromDatabaseConfigForm.get('aggregation').value === AggregationType.NONE; + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/originator-attributes-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/originator-attributes-config.component.html new file mode 100644 index 0000000000..caf7c63d11 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/originator-attributes-config.component.html @@ -0,0 +1,40 @@ + +
    +
    +
    +
    tb.rulenode.originator-attributes
    +
    + tb.rulenode.at-least-one-field-required +
    +
    + + + + +
    +
    + + {{ 'tb.rulenode.tell-failure' | translate }} + +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/originator-attributes-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/originator-attributes-config.component.ts new file mode 100644 index 0000000000..b0ff9e0098 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/originator-attributes-config.component.ts @@ -0,0 +1,75 @@ +/// +/// 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, isObject, } from '@core/public-api'; +import { FormBuilder, FormGroup } from '@angular/forms'; +import { TranslateService } from '@ngx-translate/core'; +import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models'; +import { FetchTo } from '@home/components/rule-node/rule-node-config.models'; + +@Component({ + selector: 'tb-enrichment-node-originator-attributes-config', + templateUrl: './originator-attributes-config.component.html', + styleUrls: [] +}) +export class OriginatorAttributesConfigComponent extends RuleNodeConfigurationComponent { + + originatorAttributesConfigForm: FormGroup; + + constructor(public translate: TranslateService, + private fb: FormBuilder) { + super(); + } + + protected configForm(): FormGroup { + return this.originatorAttributesConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.originatorAttributesConfigForm = this.fb.group({ + tellFailureIfAbsent: [configuration.tellFailureIfAbsent, []], + fetchTo: [configuration.fetchTo, []], + attributesControl: [configuration.attributesControl, []] + }); + } + + protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + if (isObject(configuration)) { + configuration.attributesControl = { + clientAttributeNames: isDefinedAndNotNull(configuration?.clientAttributeNames) ? configuration.clientAttributeNames : [], + latestTsKeyNames: isDefinedAndNotNull(configuration?.latestTsKeyNames) ? configuration.latestTsKeyNames : [], + serverAttributeNames: isDefinedAndNotNull(configuration?.serverAttributeNames) ? configuration.serverAttributeNames : [], + sharedAttributeNames: isDefinedAndNotNull(configuration?.sharedAttributeNames) ? configuration.sharedAttributeNames : [], + getLatestValueWithTs: isDefinedAndNotNull(configuration?.getLatestValueWithTs) ? configuration.getLatestValueWithTs : false + }; + } + + return { + fetchTo: isDefinedAndNotNull(configuration?.fetchTo) ? configuration.fetchTo : FetchTo.METADATA, + tellFailureIfAbsent: isDefinedAndNotNull(configuration?.tellFailureIfAbsent) ? configuration.tellFailureIfAbsent : false, + attributesControl: isDefinedAndNotNull(configuration?.attributesControl) ? configuration.attributesControl : null + }; + } + + protected prepareOutputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + for (const key of Object.keys(configuration.attributesControl)) { + configuration[key] = configuration.attributesControl[key]; + } + delete configuration.attributesControl; + return configuration; + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/originator-fields-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/originator-fields-config.component.html new file mode 100644 index 0000000000..6ec066f230 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/originator-fields-config.component.html @@ -0,0 +1,41 @@ + +
    + + + + +
    + + {{ 'tb.rulenode.skip-empty-fields' | translate }} + +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/originator-fields-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/originator-fields-config.component.ts new file mode 100644 index 0000000000..d291bb56d3 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/originator-fields-config.component.ts @@ -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. +/// + +import { Component } from '@angular/core'; +import { deepTrim, isDefinedAndNotNull } from '@core/public-api'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { TranslateService } from '@ngx-translate/core'; +import { allowedOriginatorFields, FetchTo, SvMapOption } from '@home/components/rule-node/rule-node-config.models'; +import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models'; + +@Component({ + selector: 'tb-enrichment-node-originator-fields-config', + templateUrl: './originator-fields-config.component.html' +}) +export class OriginatorFieldsConfigComponent extends RuleNodeConfigurationComponent { + + originatorFieldsConfigForm: FormGroup; + public originatorFields: SvMapOption[] = []; + + constructor(private fb: FormBuilder, + private translate: TranslateService) { + super(); + for (const field of allowedOriginatorFields) { + this.originatorFields.push({ + value: field.value, + name: this.translate.instant(field.name) + }); + } + } + + protected configForm(): FormGroup { + return this.originatorFieldsConfigForm; + } + + protected prepareOutputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + return deepTrim(configuration); + } + + protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + return { + dataMapping: isDefinedAndNotNull(configuration?.dataMapping) ? configuration.dataMapping : null, + ignoreNullStrings: isDefinedAndNotNull(configuration?.ignoreNullStrings) ? configuration.ignoreNullStrings : null, + fetchTo: isDefinedAndNotNull(configuration?.fetchTo) ? configuration.fetchTo : FetchTo.METADATA + }; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.originatorFieldsConfigForm = this.fb.group({ + dataMapping: [configuration.dataMapping, [Validators.required]], + ignoreNullStrings: [configuration.ignoreNullStrings, []], + fetchTo: [configuration.fetchTo, []] + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/related-attributes-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/related-attributes-config.component.html new file mode 100644 index 0000000000..a7d9f74e1d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/related-attributes-config.component.html @@ -0,0 +1,62 @@ + +
    + + +
    +
    tb.rulenode.data-to-fetch
    + + + {{ data.name }} + + + + + + + + +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/related-attributes-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/related-attributes-config.component.ts new file mode 100644 index 0000000000..8446479cf4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/related-attributes-config.component.ts @@ -0,0 +1,160 @@ +/// +/// 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 { deepTrim, isDefinedAndNotNull } from '@core/public-api'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { TranslateService } from '@ngx-translate/core'; +import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models'; +import { + allowedOriginatorFields, + DataToFetch, + dataToFetchTranslations, + FetchTo, + msgMetadataLabelTranslations, + SvMapOption +} from '../rule-node-config.models'; +import { entityFields } from '@shared/models/entity.models'; + +@Component({ + selector: 'tb-enrichment-node-related-attributes-config', + templateUrl: './related-attributes-config.component.html', + styleUrls: [] +}) +export class RelatedAttributesConfigComponent extends RuleNodeConfigurationComponent { + + relatedAttributesConfigForm: FormGroup; + + protected readonly DataToFetch = DataToFetch; + + public msgMetadataLabelTranslations = msgMetadataLabelTranslations; + public originatorFields: SvMapOption[] = []; + public fetchToData = []; + + constructor(private fb: FormBuilder, + private translate: TranslateService) { + super(); + for (const field of Object.keys(allowedOriginatorFields)) { + this.originatorFields.push({ + value: allowedOriginatorFields[field].value, + name: this.translate.instant(allowedOriginatorFields[field].name) + }); + } + for (const key of dataToFetchTranslations.keys()) { + this.fetchToData.push({ + value: key, + name: this.translate.instant(dataToFetchTranslations.get(key as DataToFetch)) + }); + } + } + + protected configForm(): FormGroup { + return this.relatedAttributesConfigForm; + } + + protected prepareOutputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + if (configuration.dataToFetch === DataToFetch.FIELDS) { + configuration.dataMapping = configuration.svMap; + delete configuration.svMap; + } else { + configuration.dataMapping = configuration.kvMap; + delete configuration.kvMap; + } + + const filteDataMapping = {}; + if (configuration && configuration.dataMapping) { + for (const key of Object.keys(configuration.dataMapping)) { + filteDataMapping[key.trim()] = configuration.dataMapping[key]; + } + } + configuration.dataMapping = filteDataMapping; + delete configuration.svMap; + delete configuration.kvMap; + + return deepTrim(configuration); + } + + protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + let svMap = { + [entityFields.name.value]: `relatedEntity${this.translate.instant(entityFields.name.name)}` + }; + let kvMap = { + serialNumber: 'sn' + }; + + let dataToFetch: DataToFetch; + if (isDefinedAndNotNull(configuration?.telemetry)) { + dataToFetch = configuration.telemetry ? DataToFetch.LATEST_TELEMETRY : DataToFetch.ATTRIBUTES; + } else { + dataToFetch = isDefinedAndNotNull(configuration?.dataToFetch) ? configuration.dataToFetch : DataToFetch.ATTRIBUTES; + } + + let dataMapping; + if (isDefinedAndNotNull(configuration?.attrMapping)) { + dataMapping = configuration.attrMapping; + } else { + dataMapping = isDefinedAndNotNull(configuration?.dataMapping) ? configuration.dataMapping : null; + } + + if (dataToFetch === DataToFetch.FIELDS) { + svMap = dataMapping; + } else { + kvMap = dataMapping; + } + + return { + relationsQuery: isDefinedAndNotNull(configuration?.relationsQuery) ? configuration.relationsQuery : null, + dataToFetch, + svMap, + kvMap, + fetchTo: isDefinedAndNotNull(configuration?.fetchTo) ? configuration.fetchTo : FetchTo.METADATA + }; + } + + public selectTranslation(latestTelemetryTranslation: string, attributesTranslation: string) { + if (this.relatedAttributesConfigForm.get('dataToFetch').value === DataToFetch.LATEST_TELEMETRY) { + return latestTelemetryTranslation; + } else { + return attributesTranslation; + } + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.relatedAttributesConfigForm = this.fb.group({ + relationsQuery: [configuration.relationsQuery, [Validators.required]], + dataToFetch: [configuration.dataToFetch, []], + kvMap: [configuration.kvMap, [Validators.required]], + svMap: [configuration.svMap, [Validators.required]], + fetchTo: [configuration.fetchTo, []] + }); + } + + protected validatorTriggers(): string[] { + return ['dataToFetch']; + } + + protected updateValidators(emitEvent: boolean) { + if (this.relatedAttributesConfigForm.get('dataToFetch').value === DataToFetch.FIELDS) { + this.relatedAttributesConfigForm.get('svMap').enable({emitEvent: false}); + this.relatedAttributesConfigForm.get('kvMap').disable({emitEvent: false}); + this.relatedAttributesConfigForm.get('svMap').updateValueAndValidity(); + } else { + this.relatedAttributesConfigForm.get('svMap').disable({emitEvent: false}); + this.relatedAttributesConfigForm.get('kvMap').enable({emitEvent: false}); + this.relatedAttributesConfigForm.get('kvMap').updateValueAndValidity(); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/rule-node-core-enrichment.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/rule-node-core-enrichment.module.ts new file mode 100644 index 0000000000..2855344801 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/rule-node-core-enrichment.module.ts @@ -0,0 +1,77 @@ +/// +/// 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, Type } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { IRuleNodeConfigurationComponent, SharedModule } from '@shared/public-api'; +import { CustomerAttributesConfigComponent } from './customer-attributes-config.component'; +import { RuleNodeConfigCommonModule } from '../common/rule-node-config-common.module'; +import { EntityDetailsConfigComponent } from './entity-details-config.component'; +import { DeviceAttributesConfigComponent } from './device-attributes-config.component'; +import { OriginatorAttributesConfigComponent } from './originator-attributes-config.component'; +import { OriginatorFieldsConfigComponent } from './originator-fields-config.component'; +import { GetTelemetryFromDatabaseConfigComponent } from './get-telemetry-from-database-config.component'; +import { RelatedAttributesConfigComponent } from './related-attributes-config.component'; +import { TenantAttributesConfigComponent } from './tenant-attributes-config.component'; +import { CalculateDeltaConfigComponent } from './calculate-delta-config.component'; +import { FetchDeviceCredentialsConfigComponent } from './fetch-device-credentials-config.component'; + +@NgModule({ + declarations: [ + CustomerAttributesConfigComponent, + EntityDetailsConfigComponent, + DeviceAttributesConfigComponent, + OriginatorAttributesConfigComponent, + OriginatorFieldsConfigComponent, + GetTelemetryFromDatabaseConfigComponent, + RelatedAttributesConfigComponent, + TenantAttributesConfigComponent, + CalculateDeltaConfigComponent, + FetchDeviceCredentialsConfigComponent + ], + imports: [ + CommonModule, + SharedModule, + RuleNodeConfigCommonModule + ], + exports: [ + CustomerAttributesConfigComponent, + EntityDetailsConfigComponent, + DeviceAttributesConfigComponent, + OriginatorAttributesConfigComponent, + OriginatorFieldsConfigComponent, + GetTelemetryFromDatabaseConfigComponent, + RelatedAttributesConfigComponent, + TenantAttributesConfigComponent, + CalculateDeltaConfigComponent, + FetchDeviceCredentialsConfigComponent + ] +}) +export class RuleNodeCoreEnrichmentModule { +} + +export const ruleNodeEnrichmentConfigComponentsMap: Record> = { + 'tbEnrichmentNodeCalculateDeltaConfig': CalculateDeltaConfigComponent, + 'tbEnrichmentNodeCustomerAttributesConfig': CustomerAttributesConfigComponent, + 'tbEnrichmentNodeDeviceAttributesConfig': DeviceAttributesConfigComponent, + 'tbEnrichmentNodeEntityDetailsConfig': EntityDetailsConfigComponent, + 'tbEnrichmentNodeFetchDeviceCredentialsConfig': FetchDeviceCredentialsConfigComponent, + 'tbEnrichmentNodeGetTelemetryFromDatabase': GetTelemetryFromDatabaseConfigComponent, + 'tbEnrichmentNodeOriginatorAttributesConfig': OriginatorAttributesConfigComponent, + 'tbEnrichmentNodeOriginatorFieldsConfig': OriginatorFieldsConfigComponent, + 'tbEnrichmentNodeRelatedAttributesConfig': RelatedAttributesConfigComponent, + 'tbEnrichmentNodeTenantAttributesConfig': TenantAttributesConfigComponent +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/tenant-attributes-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/tenant-attributes-config.component.html new file mode 100644 index 0000000000..abdc84ed63 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/tenant-attributes-config.component.html @@ -0,0 +1,45 @@ + +
    +
    tb.rulenode.mapping-of-tenant
    +
    +
    + + + {{ data.name }} + + +
    +
    + + + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/tenant-attributes-config.component.scss b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/tenant-attributes-config.component.scss new file mode 100644 index 0000000000..4943a8fa07 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/tenant-attributes-config.component.scss @@ -0,0 +1,21 @@ +/** + * 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 { + .fetch-to-data-toggle { + max-width: 420px; + width: 100%; + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/tenant-attributes-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/tenant-attributes-config.component.ts new file mode 100644 index 0000000000..2c05d3369f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/tenant-attributes-config.component.ts @@ -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 } from '@angular/core'; +import { isDefinedAndNotNull } from '@core/public-api'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { TranslateService } from '@ngx-translate/core'; +import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models'; +import { DataToFetch, dataToFetchTranslations, FetchTo } from '../rule-node-config.models'; + +@Component({ + selector: 'tb-enrichment-node-tenant-attributes-config', + templateUrl: './tenant-attributes-config.component.html', + styleUrls: ['./tenant-attributes-config.component.scss'] +}) +export class TenantAttributesConfigComponent extends RuleNodeConfigurationComponent { + + tenantAttributesConfigForm: FormGroup; + public fetchToData = []; + + constructor(private fb: FormBuilder, + private translate: TranslateService) { + super(); + for (const key of dataToFetchTranslations.keys()) { + if (key !== DataToFetch.FIELDS) { + this.fetchToData.push({ + value: key, + name: this.translate.instant(dataToFetchTranslations.get(key as DataToFetch)) + }); + } + } + } + + protected configForm(): FormGroup { + return this.tenantAttributesConfigForm; + } + + protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + let dataToFetch: DataToFetch; + if (isDefinedAndNotNull(configuration?.telemetry)) { + dataToFetch = configuration.telemetry ? DataToFetch.LATEST_TELEMETRY : DataToFetch.ATTRIBUTES; + } else { + dataToFetch = isDefinedAndNotNull(configuration?.dataToFetch) ? configuration.dataToFetch : DataToFetch.ATTRIBUTES; + } + + let dataMapping; + if (isDefinedAndNotNull(configuration?.attrMapping)) { + dataMapping = configuration.attrMapping; + } else { + dataMapping = isDefinedAndNotNull(configuration?.dataMapping) ? configuration.dataMapping : null; + } + + return { + dataToFetch, + dataMapping, + fetchTo: isDefinedAndNotNull(configuration?.fetchTo) ? configuration.fetchTo : FetchTo.METADATA + }; + } + + public selectTranslation(latestTelemetryTranslation: string, attributesTranslation: string) { + if (this.tenantAttributesConfigForm.get('dataToFetch').value === DataToFetch.LATEST_TELEMETRY) { + return latestTelemetryTranslation; + } else { + return attributesTranslation; + } + } + + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.tenantAttributesConfigForm = this.fb.group({ + dataToFetch: [configuration.dataToFetch, []], + dataMapping: [configuration.dataMapping, [Validators.required]], + fetchTo: [configuration.fetchTo, []] + }); + } + + protected readonly DataToFetch = DataToFetch; +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/azure-iot-hub-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/azure-iot-hub-config.component.html new file mode 100644 index 0000000000..44fe7d2ebd --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/azure-iot-hub-config.component.html @@ -0,0 +1,122 @@ + +
    + + tb.rulenode.topic + + + {{ 'tb.rulenode.topic-required' | translate }} + + tb.rulenode.general-pattern-hint + + + tb.rulenode.hostname + + + {{ 'tb.rulenode.hostname-required' | translate }} + + + + tb.rulenode.device-id + + + {{ 'tb.rulenode.device-id-required' | translate }} + + + + + + tb.rulenode.credentials + + {{ azureIotHubCredentialsTypeTranslationsMap.get(azureIotHubConfigForm.get('credentials.type').value) | translate }} + + +
    + + tb.rulenode.credentials-type + + + {{ azureIotHubCredentialsTypeTranslationsMap.get(credentialsType) | translate }} + + + + {{ 'tb.rulenode.credentials-type-required' | translate }} + + +
    + + + + + tb.rulenode.sas-key + + + + {{ 'tb.rulenode.sas-key-required' | translate }} + + + + + + + + + + + + + + tb.rulenode.private-key-password + + + + +
    +
    +
    +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/azure-iot-hub-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/external/azure-iot-hub-config.component.ts new file mode 100644 index 0000000000..72864610f5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/azure-iot-hub-config.component.ts @@ -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. +/// + +import { Component } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models'; +import { + AzureIotHubCredentialsType, + azureIotHubCredentialsTypes, + azureIotHubCredentialsTypeTranslations +} from '@home/components/rule-node/rule-node-config.models'; + +@Component({ + selector: 'tb-external-node-azure-iot-hub-config', + templateUrl: './azure-iot-hub-config.component.html', + styleUrls: ['./mqtt-config.component.scss'] +}) +export class AzureIotHubConfigComponent extends RuleNodeConfigurationComponent { + + azureIotHubConfigForm: UntypedFormGroup; + + allAzureIotHubCredentialsTypes = azureIotHubCredentialsTypes; + azureIotHubCredentialsTypeTranslationsMap = azureIotHubCredentialsTypeTranslations; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.azureIotHubConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.azureIotHubConfigForm = this.fb.group({ + topicPattern: [configuration ? configuration.topicPattern : null, [Validators.required]], + host: [configuration ? configuration.host : null, [Validators.required]], + port: [configuration ? configuration.port : null, [Validators.required, Validators.min(1), Validators.max(65535)]], + connectTimeoutSec: [configuration ? configuration.connectTimeoutSec : null, + [Validators.required, Validators.min(1), Validators.max(200)]], + clientId: [configuration ? configuration.clientId : null, [Validators.required]], + cleanSession: [configuration ? configuration.cleanSession : false, []], + ssl: [configuration ? configuration.ssl : false, []], + credentials: this.fb.group( + { + type: [configuration && configuration.credentials ? configuration.credentials.type : null, [Validators.required]], + sasKey: [configuration && configuration.credentials ? configuration.credentials.sasKey : null, []], + caCert: [configuration && configuration.credentials ? configuration.credentials.caCert : null, []], + caCertFileName: [configuration && configuration.credentials ? configuration.credentials.caCertFileName : null, []], + privateKey: [configuration && configuration.credentials ? configuration.credentials.privateKey : null, []], + privateKeyFileName: [configuration && configuration.credentials ? configuration.credentials.privateKeyFileName : null, []], + cert: [configuration && configuration.credentials ? configuration.credentials.cert : null, []], + certFileName: [configuration && configuration.credentials ? configuration.credentials.certFileName : null, []], + password: [configuration && configuration.credentials ? configuration.credentials.password : null, []], + } + ) + }); + } + + protected prepareOutputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + const credentialsType: AzureIotHubCredentialsType = configuration.credentials.type; + if (credentialsType === 'sas') { + configuration.credentials = { + type: credentialsType, + sasKey: configuration.credentials.sasKey, + caCert: configuration.credentials.caCert, + caCertFileName: configuration.credentials.caCertFileName + }; + } + return configuration; + } + + protected validatorTriggers(): string[] { + return ['credentials.type']; + } + + protected updateValidators(emitEvent: boolean) { + const credentialsControl = this.azureIotHubConfigForm.get('credentials'); + const credentialsType: AzureIotHubCredentialsType = credentialsControl.get('type').value; + if (emitEvent) { + credentialsControl.reset({ type: credentialsType }, {emitEvent: false}); + } + credentialsControl.get('sasKey').setValidators([]); + credentialsControl.get('privateKey').setValidators([]); + credentialsControl.get('privateKeyFileName').setValidators([]); + credentialsControl.get('cert').setValidators([]); + credentialsControl.get('certFileName').setValidators([]); + switch (credentialsType) { + case 'sas': + credentialsControl.get('sasKey').setValidators([Validators.required]); + break; + case 'cert.PEM': + credentialsControl.get('privateKey').setValidators([Validators.required]); + credentialsControl.get('privateKeyFileName').setValidators([Validators.required]); + credentialsControl.get('cert').setValidators([Validators.required]); + credentialsControl.get('certFileName').setValidators([Validators.required]); + break; + } + credentialsControl.get('sasKey').updateValueAndValidity({emitEvent}); + credentialsControl.get('privateKey').updateValueAndValidity({emitEvent}); + credentialsControl.get('privateKeyFileName').updateValueAndValidity({emitEvent}); + credentialsControl.get('cert').updateValueAndValidity({emitEvent}); + credentialsControl.get('certFileName').updateValueAndValidity({emitEvent}); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/kafka-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/kafka-config.component.html new file mode 100644 index 0000000000..6bb1438a00 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/kafka-config.component.html @@ -0,0 +1,111 @@ + +
    + + tb.rulenode.topic-pattern + + + {{ 'tb.rulenode.topic-pattern-required' | translate }} + + tb.rulenode.general-pattern-hint + + + tb.rulenode.key-pattern + + tb.rulenode.general-pattern-hint + +
    tb.rulenode.key-pattern-hint
    + + tb.rulenode.bootstrap-servers + + + {{ 'tb.rulenode.bootstrap-servers-required' | translate }} + + + + tb.rulenode.retries + + + {{ 'tb.rulenode.min-retries-message' | translate }} + + + + tb.rulenode.batch-size-bytes + + + {{ 'tb.rulenode.min-batch-size-bytes-message' | translate }} + + + + tb.rulenode.linger-ms + + + {{ 'tb.rulenode.min-linger-ms-message' | translate }} + + + + tb.rulenode.buffer-memory-bytes + + + {{ 'tb.rulenode.min-buffer-memory-bytes-message' | translate }} + + + + tb.rulenode.acks + + + {{ ackValue }} + + + + + tb.rulenode.key-serializer + + + {{ 'tb.rulenode.key-serializer-required' | translate }} + + + + tb.rulenode.value-serializer + + + {{ 'tb.rulenode.value-serializer-required' | translate }} + + + + + + + {{ 'tb.rulenode.add-metadata-key-values-as-kafka-headers' | translate }} + +
    tb.rulenode.add-metadata-key-values-as-kafka-headers-hint
    + + tb.rulenode.charset-encoding + + + {{ ToByteStandartCharsetTypeTranslationMap.get(charset) | translate }} + + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/kafka-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/external/kafka-config.component.ts new file mode 100644 index 0000000000..d0f97a8f9d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/kafka-config.component.ts @@ -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. +/// + +import { Component } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; +import { + ToByteStandartCharsetTypes, + ToByteStandartCharsetTypeTranslations +} from '@home/components/rule-node/rule-node-config.models'; + +@Component({ + selector: 'tb-external-node-kafka-config', + templateUrl: './kafka-config.component.html', + styleUrls: [] +}) +export class KafkaConfigComponent extends RuleNodeConfigurationComponent { + + kafkaConfigForm: UntypedFormGroup; + + ackValues: string[] = ['all', '-1', '0', '1']; + + ToByteStandartCharsetTypesValues = ToByteStandartCharsetTypes; + ToByteStandartCharsetTypeTranslationMap = ToByteStandartCharsetTypeTranslations; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.kafkaConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.kafkaConfigForm = this.fb.group({ + topicPattern: [configuration ? configuration.topicPattern : null, [Validators.required]], + keyPattern: [configuration ? configuration.keyPattern : null], + bootstrapServers: [configuration ? configuration.bootstrapServers : null, [Validators.required]], + retries: [configuration ? configuration.retries : null, [Validators.min(0)]], + batchSize: [configuration ? configuration.batchSize : null, [Validators.min(0)]], + linger: [configuration ? configuration.linger : null, [Validators.min(0)]], + bufferMemory: [configuration ? configuration.bufferMemory : null, [Validators.min(0)]], + acks: [configuration ? configuration.acks : null, [Validators.required]], + keySerializer: [configuration ? configuration.keySerializer : null, [Validators.required]], + valueSerializer: [configuration ? configuration.valueSerializer : null, [Validators.required]], + otherProperties: [configuration ? configuration.otherProperties : null, []], + addMetadataKeyValuesAsKafkaHeaders: [configuration ? configuration.addMetadataKeyValuesAsKafkaHeaders : false, []], + kafkaHeadersCharset: [configuration ? configuration.kafkaHeadersCharset : null, []] + }); + } + + protected validatorTriggers(): string[] { + return ['addMetadataKeyValuesAsKafkaHeaders']; + } + + protected updateValidators(emitEvent: boolean) { + const addMetadataKeyValuesAsKafkaHeaders: boolean = this.kafkaConfigForm.get('addMetadataKeyValuesAsKafkaHeaders').value; + if (addMetadataKeyValuesAsKafkaHeaders) { + this.kafkaConfigForm.get('kafkaHeadersCharset').setValidators([Validators.required]); + } else { + this.kafkaConfigForm.get('kafkaHeadersCharset').setValidators([]); + } + this.kafkaConfigForm.get('kafkaHeadersCharset').updateValueAndValidity({emitEvent}); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/lambda-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/lambda-config.component.html new file mode 100644 index 0000000000..a6907082fc --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/lambda-config.component.html @@ -0,0 +1,115 @@ + +
    +
    +
    +
    tb.rulenode.function-configuration
    +
    + + +
    + + {{'tb.rulenode.function-name' | translate}} + + + {{'tb.rulenode.function-name-required' | translate}} + + + + {{'tb.rulenode.qualifier' | translate}} + + tb.rulenode.qualifier-hint + +
    +
    + +
    + + + tb.rulenode.aws-credentials + +
    + + tb.rulenode.aws-access-key-id + + + {{ 'tb.rulenode.aws-access-key-id-required' | translate }} + + + + tb.rulenode.aws-secret-access-key + + + {{ 'tb.rulenode.aws-secret-access-key-required' | translate }} + + + + tb.rulenode.aws-region + + + {{ 'tb.rulenode.aws-region-required' | translate }} + + +
    +
    +
    +
    + + + tb.rulenode.advanced-settings + +
    +
    + + tb.rulenode.connection-timeout + + + {{ 'tb.rulenode.connection-timeout-required' | translate }} + + + {{ 'tb.rulenode.connection-timeout-min' | translate }} + + help + + + tb.rulenode.request-timeout + + + {{ 'tb.rulenode.request-timeout-required' | translate }} + + + {{ 'tb.rulenode.request-timeout-min' | translate }} + + help + +
    +
    + + {{ 'tb.rulenode.tell-failure-aws-lambda' | translate }} + +
    +
    +
    +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/lambda-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/external/lambda-config.component.ts new file mode 100644 index 0000000000..3270e3b51e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/lambda-config.component.ts @@ -0,0 +1,50 @@ +/// +/// 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-external-node-lambda-config', + templateUrl: './lambda-config.component.html', + styleUrls: [] +}) +export class LambdaConfigComponent extends RuleNodeConfigurationComponent { + + lambdaConfigForm: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.lambdaConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.lambdaConfigForm = this.fb.group({ + functionName: [configuration ? configuration.functionName : null, [Validators.required]], + qualifier: [configuration ? configuration.qualifier : null, []], + accessKey: [configuration ? configuration.accessKey : null, [Validators.required]], + secretKey: [configuration ? configuration.secretKey : null, [Validators.required]], + region: [configuration ? configuration.region : null, [Validators.required]], + connectionTimeout: [configuration ? configuration.connectionTimeout : null, [Validators.required, Validators.min(0)]], + requestTimeout: [configuration ? configuration.requestTimeout : null, [Validators.required, Validators.min(0)]], + tellFailureIfFuncThrowsExc: [configuration ? configuration.tellFailureIfFuncThrowsExc : false, []] + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/mqtt-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/mqtt-config.component.html new file mode 100644 index 0000000000..989a8829b7 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/mqtt-config.component.html @@ -0,0 +1,85 @@ + +
    + + tb.rulenode.topic-pattern + + + {{ 'tb.rulenode.topic-pattern-required' | translate }} + + tb.rulenode.general-pattern-hint + +
    + + tb.rulenode.host + + + {{ 'tb.rulenode.host-required' | translate }} + + + + tb.rulenode.port + + + {{ 'tb.rulenode.port-required' | translate }} + + + {{ 'tb.rulenode.port-range' | translate }} + + + {{ 'tb.rulenode.port-range' | translate }} + + + + tb.rulenode.connect-timeout + + + {{ 'tb.rulenode.connect-timeout-required' | translate }} + + + {{ 'tb.rulenode.connect-timeout-range' | translate }} + + + {{ 'tb.rulenode.connect-timeout-range' | translate }} + + +
    + + tb.rulenode.client-id + + {{'tb.rulenode.client-id-hint' | translate}} + + + {{ 'tb.rulenode.append-client-id-suffix' | translate }} + +
    {{ "tb.rulenode.client-id-suffix-hint" | translate }}
    + + {{ 'tb.rulenode.parse-to-plain-text' | translate }} + +
    {{ "tb.rulenode.parse-to-plain-text-hint" | translate }}
    + + {{ 'tb.rulenode.clean-session' | translate }} + + + {{ "tb.rulenode.retained-message" | translate }} + + + {{ 'tb.rulenode.enable-ssl' | translate }} + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/mqtt-config.component.scss b/ui-ngx/src/app/modules/home/components/rule-node/external/mqtt-config.component.scss new file mode 100644 index 0000000000..0bd9d8e62c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/mqtt-config.component.scss @@ -0,0 +1,20 @@ +/** + * 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 { + .tb-mqtt-credentials-panel-group { + margin: 0 6px; + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/mqtt-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/external/mqtt-config.component.ts new file mode 100644 index 0000000000..7edfc6f14d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/mqtt-config.component.ts @@ -0,0 +1,71 @@ +/// +/// 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 { isNotEmptyStr } from '@core/public-api'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; + +@Component({ + selector: 'tb-external-node-mqtt-config', + templateUrl: './mqtt-config.component.html', + styleUrls: ['./mqtt-config.component.scss'] +}) +export class MqttConfigComponent extends RuleNodeConfigurationComponent { + + mqttConfigForm: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.mqttConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.mqttConfigForm = this.fb.group({ + topicPattern: [configuration ? configuration.topicPattern : null, [Validators.required]], + host: [configuration ? configuration.host : null, [Validators.required]], + port: [configuration ? configuration.port : null, [Validators.required, Validators.min(1), Validators.max(65535)]], + connectTimeoutSec: [configuration ? configuration.connectTimeoutSec : null, + [Validators.required, Validators.min(1), Validators.max(200)]], + clientId: [configuration ? configuration.clientId : null, []], + appendClientIdSuffix: [{ + value: configuration ? configuration.appendClientIdSuffix : false, + disabled: !(configuration && isNotEmptyStr(configuration.clientId)) + }, []], + parseToPlainText: [configuration ? configuration.parseToPlainText : false, []], + cleanSession: [configuration ? configuration.cleanSession : false, []], + retainedMessage: [configuration ? configuration.retainedMessage : false, []], + ssl: [configuration ? configuration.ssl : false, []], + credentials: [configuration ? configuration.credentials : null, []] + }); + } + + protected updateValidators(emitEvent: boolean) { + if (isNotEmptyStr(this.mqttConfigForm.get('clientId').value)) { + this.mqttConfigForm.get('appendClientIdSuffix').enable({emitEvent: false}); + } else { + this.mqttConfigForm.get('appendClientIdSuffix').disable({emitEvent: false}); + } + this.mqttConfigForm.get('appendClientIdSuffix').updateValueAndValidity({emitEvent}); + } + + protected validatorTriggers(): string[] { + return ['clientId']; + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/notification-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/notification-config.component.html new file mode 100644 index 0000000000..ee031b1237 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/notification-config.component.html @@ -0,0 +1,34 @@ + +
    + + + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/notification-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/external/notification-config.component.ts new file mode 100644 index 0000000000..2b2fcfb319 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/notification-config.component.ts @@ -0,0 +1,48 @@ +/// +/// 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 { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; +import { NotificationType } from '@shared/models/notification.models'; +import { EntityType } from '@shared/models/entity-type.models'; + +@Component({ + selector: 'tb-external-node-notification-config', + templateUrl: './notification-config.component.html', + styleUrls: [] +}) +export class NotificationConfigComponent extends RuleNodeConfigurationComponent { + + notificationConfigForm: FormGroup; + notificationType = NotificationType; + entityType = EntityType; + + constructor(private fb: FormBuilder) { + super(); + } + + protected configForm(): FormGroup { + return this.notificationConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.notificationConfigForm = this.fb.group({ + templateId: [configuration ? configuration.templateId : null, [Validators.required]], + targets: [configuration ? configuration.targets : [], [Validators.required]], + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/pubsub-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/pubsub-config.component.html new file mode 100644 index 0000000000..eb5c700400 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/pubsub-config.component.html @@ -0,0 +1,53 @@ + +
    + + tb.rulenode.gcp-project-id + + + {{ 'tb.rulenode.gcp-project-id-required' | translate }} + + + + tb.rulenode.pubsub-topic-name + + + {{ 'tb.rulenode.pubsub-topic-name-required' | translate }} + + + + + +
    + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/pubsub-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/external/pubsub-config.component.ts new file mode 100644 index 0000000000..f7c65537e0 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/pubsub-config.component.ts @@ -0,0 +1,47 @@ +/// +/// 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-external-node-pub-sub-config', + templateUrl: './pubsub-config.component.html', + styleUrls: [] +}) +export class PubSubConfigComponent extends RuleNodeConfigurationComponent { + + pubSubConfigForm: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.pubSubConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.pubSubConfigForm = this.fb.group({ + projectId: [configuration ? configuration.projectId : null, [Validators.required]], + topicName: [configuration ? configuration.topicName : null, [Validators.required]], + serviceAccountKey: [configuration ? configuration.serviceAccountKey : null, [Validators.required]], + serviceAccountKeyFileName: [configuration ? configuration.serviceAccountKeyFileName : null, [Validators.required]], + messageAttributes: [configuration ? configuration.messageAttributes : null, []] + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/rabbit-mq-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/rabbit-mq-config.component.html new file mode 100644 index 0000000000..0ef6b40877 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/rabbit-mq-config.component.html @@ -0,0 +1,96 @@ + +
    + + tb.rulenode.exchange-name-pattern + + + + tb.rulenode.routing-key-pattern + + + + tb.rulenode.message-properties + + + {{ property }} + + + +
    + + tb.rulenode.host + + + {{ 'tb.rulenode.host-required' | translate }} + + + + tb.rulenode.port + + + {{ 'tb.rulenode.port-required' | translate }} + + + {{ 'tb.rulenode.port-range' | translate }} + + + {{ 'tb.rulenode.port-range' | translate }} + + +
    + + tb.rulenode.virtual-host + + + + tb.rulenode.username + + + + tb.rulenode.password + + + + + {{ 'tb.rulenode.automatic-recovery' | translate }} + + + tb.rulenode.connection-timeout-ms + + + {{ 'tb.rulenode.min-connection-timeout-ms-message' | translate }} + + + + tb.rulenode.handshake-timeout-ms + + + {{ 'tb.rulenode.min-handshake-timeout-ms-message' | translate }} + + + + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/rabbit-mq-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/external/rabbit-mq-config.component.ts new file mode 100644 index 0000000000..89558bbe7f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/rabbit-mq-config.component.ts @@ -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 { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; + +@Component({ + selector: 'tb-external-node-rabbit-mq-config', + templateUrl: './rabbit-mq-config.component.html', + styleUrls: [] +}) +export class RabbitMqConfigComponent extends RuleNodeConfigurationComponent { + + rabbitMqConfigForm: UntypedFormGroup; + + messageProperties: string[] = [ + null, + 'BASIC', + 'TEXT_PLAIN', + 'MINIMAL_BASIC', + 'MINIMAL_PERSISTENT_BASIC', + 'PERSISTENT_BASIC', + 'PERSISTENT_TEXT_PLAIN' + ]; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.rabbitMqConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.rabbitMqConfigForm = this.fb.group({ + exchangeNamePattern: [configuration ? configuration.exchangeNamePattern : null, []], + routingKeyPattern: [configuration ? configuration.routingKeyPattern : null, []], + messageProperties: [configuration ? configuration.messageProperties : null, []], + host: [configuration ? configuration.host : null, [Validators.required]], + port: [configuration ? configuration.port : null, [Validators.required, Validators.min(1), Validators.max(65535)]], + virtualHost: [configuration ? configuration.virtualHost : null, []], + username: [configuration ? configuration.username : null, []], + password: [configuration ? configuration.password : null, []], + automaticRecoveryEnabled: [configuration ? configuration.automaticRecoveryEnabled : false, []], + connectionTimeout: [configuration ? configuration.connectionTimeout : null, [Validators.min(0)]], + handshakeTimeout: [configuration ? configuration.handshakeTimeout : null, [Validators.min(0)]], + clientProperties: [configuration ? configuration.clientProperties : null, []] + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/rest-api-call-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/rest-api-call-config.component.html new file mode 100644 index 0000000000..5dfca5144a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/rest-api-call-config.component.html @@ -0,0 +1,130 @@ + +
    + + tb.rulenode.endpoint-url-pattern + + + {{ 'tb.rulenode.endpoint-url-pattern-required' | translate }} + + tb.rulenode.general-pattern-hint + + + tb.rulenode.request-method + + + {{ requestType }} + + + + + {{ 'tb.rulenode.enable-proxy' | translate }} + + + {{ 'tb.rulenode.use-simple-client-http-factory' | translate }} + + + {{ 'tb.rulenode.parse-to-plain-text' | translate }} + +
    tb.rulenode.parse-to-plain-text-hint
    + + {{ 'tb.rulenode.ignore-request-body' | translate }} + +
    + + {{ 'tb.rulenode.use-system-proxy-properties' | translate }} + +
    +
    + + tb.rulenode.proxy-scheme + + + {{ proxyScheme }} + + + + + tb.rulenode.proxy-host + + + {{ 'tb.rulenode.proxy-host-required' | translate }} + + + + tb.rulenode.proxy-port + + + {{ 'tb.rulenode.proxy-port-required' | translate }} + + + {{ 'tb.rulenode.proxy-port-range' | translate }} + + +
    + + tb.rulenode.proxy-user + + + + tb.rulenode.proxy-password + + +
    +
    + + tb.rulenode.read-timeout + + tb.rulenode.read-timeout-hint + + {{ 'tb.rulenode.int-range' | translate }} + + + + tb.rulenode.max-parallel-requests-count + + tb.rulenode.max-parallel-requests-count-hint + + {{ 'tb.rulenode.int-range' | translate }} + + + + tb.rulenode.max-response-size + + tb.rulenode.max-response-size-hint + + {{ 'tb.rulenode.memory-buffer-size-range' | translate: { max: MemoryBufferSizeInKbLimit } }} + + + +
    + + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/rest-api-call-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/external/rest-api-call-config.component.ts new file mode 100644 index 0000000000..fc6583c096 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/rest-api-call-config.component.ts @@ -0,0 +1,95 @@ +/// +/// 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 { HttpRequestType, IntLimit } from '../rule-node-config.models'; + +@Component({ + selector: 'tb-external-node-rest-api-call-config', + templateUrl: './rest-api-call-config.component.html', + styleUrls: [] +}) +export class RestApiCallConfigComponent extends RuleNodeConfigurationComponent { + + restApiCallConfigForm: UntypedFormGroup; + + readonly proxySchemes: string[] = ['http', 'https']; + readonly httpRequestTypes = Object.keys(HttpRequestType); + readonly MemoryBufferSizeInKbLimit = 25000; + readonly IntLimit = IntLimit; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.restApiCallConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.restApiCallConfigForm = this.fb.group({ + restEndpointUrlPattern: [configuration ? configuration.restEndpointUrlPattern : null, [Validators.required]], + requestMethod: [configuration ? configuration.requestMethod : null, [Validators.required]], + useSimpleClientHttpFactory: [configuration ? configuration.useSimpleClientHttpFactory : false, []], + parseToPlainText: [configuration ? configuration.parseToPlainText : false, []], + ignoreRequestBody: [configuration ? configuration.ignoreRequestBody : false, []], + enableProxy: [configuration ? configuration.enableProxy : false, []], + useSystemProxyProperties: [configuration ? configuration.enableProxy : false, []], + proxyScheme: [configuration ? configuration.proxyHost : null, []], + proxyHost: [configuration ? configuration.proxyHost : null, []], + proxyPort: [configuration ? configuration.proxyPort : null, []], + proxyUser: [configuration ? configuration.proxyUser :null, []], + proxyPassword: [configuration ? configuration.proxyPassword :null, []], + readTimeoutMs: [configuration ? configuration.readTimeoutMs : null, [Validators.min(0), Validators.max(IntLimit)]], + maxParallelRequestsCount: [configuration ? configuration.maxParallelRequestsCount : null, [Validators.min(0), Validators.max(IntLimit)]], + headers: [configuration ? configuration.headers : null, []], + credentials: [configuration ? configuration.credentials : null, []], + maxInMemoryBufferSizeInKb: [configuration ? configuration.maxInMemoryBufferSizeInKb : null, [Validators.min(1), Validators.max(this.MemoryBufferSizeInKbLimit)]] + }); + } + + protected validatorTriggers(): string[] { + return ['useSimpleClientHttpFactory', 'enableProxy', 'useSystemProxyProperties']; + } + + protected updateValidators(emitEvent: boolean) { + const useSimpleClientHttpFactory: boolean = this.restApiCallConfigForm.get('useSimpleClientHttpFactory').value; + const enableProxy: boolean = this.restApiCallConfigForm.get('enableProxy').value; + const useSystemProxyProperties: boolean = this.restApiCallConfigForm.get('useSystemProxyProperties').value; + + if (enableProxy && !useSystemProxyProperties) { + this.restApiCallConfigForm.get('proxyHost').setValidators(enableProxy ? [Validators.required] : []); + this.restApiCallConfigForm.get('proxyPort').setValidators(enableProxy ? + [Validators.required, Validators.min(1), Validators.max(65535)] : []); + } else { + this.restApiCallConfigForm.get('proxyHost').setValidators([]); + this.restApiCallConfigForm.get('proxyPort').setValidators([]); + + if (useSimpleClientHttpFactory) { + this.restApiCallConfigForm.get('readTimeoutMs').setValidators([]); + } else { + this.restApiCallConfigForm.get('readTimeoutMs').setValidators([Validators.min(0), Validators.max(IntLimit)]); + } + } + + this.restApiCallConfigForm.get('readTimeoutMs').updateValueAndValidity({emitEvent}); + this.restApiCallConfigForm.get('proxyHost').updateValueAndValidity({emitEvent}); + this.restApiCallConfigForm.get('proxyPort').updateValueAndValidity({emitEvent}); + this.restApiCallConfigForm.get('credentials').updateValueAndValidity({emitEvent}); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/rule-node-config-external.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/external/rule-node-config-external.module.ts new file mode 100644 index 0000000000..c170d9ccd6 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/rule-node-config-external.module.ts @@ -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 { NgModule, Type } from '@angular/core'; +import { SnsConfigComponent } from './sns-config.component'; +import { SqsConfigComponent } from './sqs-config.component'; +import { PubSubConfigComponent } from './pubsub-config.component'; +import { KafkaConfigComponent } from './kafka-config.component'; +import { MqttConfigComponent } from './mqtt-config.component'; +import { NotificationConfigComponent } from './notification-config.component'; +import { RabbitMqConfigComponent } from './rabbit-mq-config.component'; +import { RestApiCallConfigComponent } from './rest-api-call-config.component'; +import { SendEmailConfigComponent } from './send-email-config.component'; +import { AzureIotHubConfigComponent } from './azure-iot-hub-config.component'; +import { SendSmsConfigComponent } from './send-sms-config.component'; +import { CommonModule } from '@angular/common'; +import { IRuleNodeConfigurationComponent, SharedModule } from '@shared/public-api'; +import { HomeComponentsModule } from '@home/components/public-api'; +import { RuleNodeConfigCommonModule } from '../common/rule-node-config-common.module'; +import { SlackConfigComponent } from './slack-config.component'; +import { LambdaConfigComponent } from './lambda-config.component'; + +@NgModule({ + declarations: [ + SnsConfigComponent, + SqsConfigComponent, + LambdaConfigComponent, + PubSubConfigComponent, + KafkaConfigComponent, + MqttConfigComponent, + NotificationConfigComponent, + RabbitMqConfigComponent, + RestApiCallConfigComponent, + SendEmailConfigComponent, + AzureIotHubConfigComponent, + SendSmsConfigComponent, + SlackConfigComponent + ], + imports: [ + CommonModule, + SharedModule, + HomeComponentsModule, + RuleNodeConfigCommonModule + ], + exports: [ + SnsConfigComponent, + SqsConfigComponent, + LambdaConfigComponent, + PubSubConfigComponent, + KafkaConfigComponent, + MqttConfigComponent, + NotificationConfigComponent, + RabbitMqConfigComponent, + RestApiCallConfigComponent, + SendEmailConfigComponent, + AzureIotHubConfigComponent, + SendSmsConfigComponent, + SlackConfigComponent + ] +}) +export class RuleNodeConfigExternalModule { +} + +export const ruleNodeExternalConfigComponentsMap: Record> = { + 'tbExternalNodeAzureIotHubConfig': AzureIotHubConfigComponent, + 'tbExternalNodeKafkaConfig': KafkaConfigComponent, + 'tbExternalNodeLambdaConfig': LambdaConfigComponent, + 'tbExternalNodeMqttConfig': MqttConfigComponent, + 'tbExternalNodeNotificationConfig': NotificationConfigComponent, + 'tbExternalNodePubSubConfig': PubSubConfigComponent, + 'tbExternalNodeRabbitMqConfig': RabbitMqConfigComponent, + 'tbExternalNodeRestApiCallConfig': RestApiCallConfigComponent, + 'tbExternalNodeSendEmailConfig': SendEmailConfigComponent, + 'tbExternalNodeSendSmsConfig': SendSmsConfigComponent, + 'tbExternalNodeSlackConfig': SlackConfigComponent, + 'tbExternalNodeSnsConfig': SnsConfigComponent, + 'tbExternalNodeSqsConfig': SqsConfigComponent +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/send-email-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/send-email-config.component.html new file mode 100644 index 0000000000..cbc4ec7a4a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/send-email-config.component.html @@ -0,0 +1,116 @@ + +
    + + {{ 'tb.rulenode.use-system-smtp-settings' | translate }} + +
    + + tb.rulenode.smtp-protocol + + + {{ smtpProtocol.toUpperCase() }} + + + +
    + + tb.rulenode.smtp-host + + + {{ 'tb.rulenode.smtp-host-required' | translate }} + + + + tb.rulenode.smtp-port + + + {{ 'tb.rulenode.smtp-port-required' | translate }} + + + {{ 'tb.rulenode.smtp-port-range' | translate }} + + + {{ 'tb.rulenode.smtp-port-range' | translate }} + + +
    + + tb.rulenode.timeout-msec + + + {{ 'tb.rulenode.timeout-required' | translate }} + + + {{ 'tb.rulenode.min-timeout-msec-message' | translate }} + + + + {{ 'tb.rulenode.enable-tls' | translate }} + + + tb.rulenode.tls-version + + + {{ tlsVersion }} + + + + + {{ 'tb.rulenode.enable-proxy' | translate }} + +
    +
    + + tb.rulenode.proxy-host + + + {{ 'tb.rulenode.proxy-host-required' | translate }} + + + + tb.rulenode.proxy-port + + + {{ 'tb.rulenode.proxy-port-required' | translate }} + + + {{ 'tb.rulenode.proxy-port-range' | translate }} + + +
    + + tb.rulenode.proxy-user + + + + tb.rulenode.proxy-password + + +
    + + tb.rulenode.username + + + + tb.rulenode.password + + + +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/send-email-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/external/send-email-config.component.ts new file mode 100644 index 0000000000..b0e77d6649 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/send-email-config.component.ts @@ -0,0 +1,95 @@ +/// +/// 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-external-node-send-email-config', + templateUrl: './send-email-config.component.html', + styleUrls: [] +}) +export class SendEmailConfigComponent extends RuleNodeConfigurationComponent { + + sendEmailConfigForm: UntypedFormGroup; + + smtpProtocols: string[] = [ + 'smtp', + 'smtps' + ]; + + tlsVersions = ['TLSv1', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3']; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.sendEmailConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.sendEmailConfigForm = this.fb.group({ + useSystemSmtpSettings: [configuration ? configuration.useSystemSmtpSettings : false, []], + smtpProtocol: [configuration ? configuration.smtpProtocol : null, []], + smtpHost: [configuration ? configuration.smtpHost : null, []], + smtpPort: [configuration ? configuration.smtpPort : null, []], + timeout: [configuration ? configuration.timeout : null, []], + enableTls: [configuration ? configuration.enableTls : false, []], + tlsVersion: [configuration ? configuration.tlsVersion : null, []], + enableProxy: [configuration ? configuration.enableProxy : false, []], + proxyHost: [configuration ? configuration.proxyHost : null, []], + proxyPort: [configuration ? configuration.proxyPort : null, []], + proxyUser: [configuration ? configuration.proxyUser :null, []], + proxyPassword: [configuration ? configuration.proxyPassword :null, []], + username: [configuration ? configuration.username : null, []], + password: [configuration ? configuration.password : null, []] + }); + } + + protected validatorTriggers(): string[] { + return ['useSystemSmtpSettings', 'enableProxy']; + } + + protected updateValidators(emitEvent: boolean) { + const useSystemSmtpSettings: boolean = this.sendEmailConfigForm.get('useSystemSmtpSettings').value; + const enableProxy: boolean = this.sendEmailConfigForm.get('enableProxy').value; + if (useSystemSmtpSettings) { + this.sendEmailConfigForm.get('smtpProtocol').setValidators([]); + this.sendEmailConfigForm.get('smtpHost').setValidators([]); + this.sendEmailConfigForm.get('smtpPort').setValidators([]); + this.sendEmailConfigForm.get('timeout').setValidators([]); + this.sendEmailConfigForm.get('proxyHost').setValidators([]); + this.sendEmailConfigForm.get('proxyPort').setValidators([]); + } else { + this.sendEmailConfigForm.get('smtpProtocol').setValidators([Validators.required]); + this.sendEmailConfigForm.get('smtpHost').setValidators([Validators.required]); + this.sendEmailConfigForm.get('smtpPort').setValidators([Validators.required, Validators.min(1), Validators.max(65535)]); + this.sendEmailConfigForm.get('timeout').setValidators([Validators.required, Validators.min(0)]); + this.sendEmailConfigForm.get('proxyHost').setValidators(enableProxy ? [Validators.required] : []); + this.sendEmailConfigForm.get('proxyPort').setValidators(enableProxy ? + [Validators.required, Validators.min(1), Validators.max(65535)] : []); + } + this.sendEmailConfigForm.get('smtpProtocol').updateValueAndValidity({emitEvent}); + this.sendEmailConfigForm.get('smtpHost').updateValueAndValidity({emitEvent}); + this.sendEmailConfigForm.get('smtpPort').updateValueAndValidity({emitEvent}); + this.sendEmailConfigForm.get('timeout').updateValueAndValidity({emitEvent}); + this.sendEmailConfigForm.get('proxyHost').updateValueAndValidity({emitEvent}); + this.sendEmailConfigForm.get('proxyPort').updateValueAndValidity({emitEvent}); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/send-sms-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/send-sms-config.component.html new file mode 100644 index 0000000000..12f9208c5d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/send-sms-config.component.html @@ -0,0 +1,43 @@ + +
    + + tb.rulenode.numbers-to-template + + + {{ 'tb.rulenode.numbers-to-template-required' | translate }} + + + + + tb.rulenode.sms-message-template + + + {{ 'tb.rulenode.sms-message-template-required' | translate }} + + tb.rulenode.general-pattern-hint + + + {{ 'tb.rulenode.use-system-sms-settings' | translate }} + + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/send-sms-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/external/send-sms-config.component.ts new file mode 100644 index 0000000000..aef2077e1c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/send-sms-config.component.ts @@ -0,0 +1,61 @@ +/// +/// 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-external-node-send-sms-config', + templateUrl: './send-sms-config.component.html', + styleUrls: [] +}) +export class SendSmsConfigComponent extends RuleNodeConfigurationComponent { + + sendSmsConfigForm: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.sendSmsConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.sendSmsConfigForm = this.fb.group({ + numbersToTemplate: [configuration ? configuration.numbersToTemplate : null, [Validators.required]], + smsMessageTemplate: [configuration ? configuration.smsMessageTemplate : null, [Validators.required]], + useSystemSmsSettings: [configuration ? configuration.useSystemSmsSettings : false, []], + smsProviderConfiguration: [configuration ? configuration.smsProviderConfiguration : null, []], + }); + } + + protected validatorTriggers(): string[] { + return ['useSystemSmsSettings']; + } + + protected updateValidators(emitEvent: boolean) { + const useSystemSmsSettings: boolean = this.sendSmsConfigForm.get('useSystemSmsSettings').value; + if (useSystemSmsSettings) { + this.sendSmsConfigForm.get('smsProviderConfiguration').setValidators([]); + } else { + this.sendSmsConfigForm.get('smsProviderConfiguration').setValidators([Validators.required]); + } + this.sendSmsConfigForm.get('smsProviderConfiguration').updateValueAndValidity({emitEvent}); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/slack-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/slack-config.component.html new file mode 100644 index 0000000000..1a466f6bf9 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/slack-config.component.html @@ -0,0 +1,49 @@ + +
    + + tb.rulenode.message-template + + + {{ 'tb.rulenode.message-template-required' | translate }} + + tb.rulenode.general-pattern-hint + + + {{ 'tb.rulenode.use-system-slack-settings' | translate }} + + + tb.rulenode.slack-api-token + + + {{ 'tb.rulenode.slack-api-token-required' | translate }} + + + + + + {{ slackChanelTypesTranslateMap.get(slackChanelType) | translate }} + + + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/slack-config.component.scss b/ui-ngx/src/app/modules/home/components/rule-node/external/slack-config.component.scss new file mode 100644 index 0000000000..524b588874 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/slack-config.component.scss @@ -0,0 +1,43 @@ +/** + * 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 { + .tb-title { + display: block; + padding-bottom: 6px; + } +} + +:host ::ng-deep { + .mat-mdc-radio-group { + display: flex; + flex-direction: row; + margin-bottom: 22px; + gap: 12px; + + .mat-mdc-radio-button { + flex: 1 1 100%; + padding: 4px; + border: 1px solid rgba(0, 0, 0, 0.12); + border-radius: 6px; + } + } + + @media screen and (max-width: 599px) { + .mat-mdc-radio-group { + flex-direction: column; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/slack-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/external/slack-config.component.ts new file mode 100644 index 0000000000..4d8cfc7a32 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/slack-config.component.ts @@ -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 { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { RuleNodeConfiguration, SlackChanelType, SlackChanelTypesTranslateMap } from '@app/shared/public-api'; +import { RuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; + +@Component({ + selector: 'tb-external-node-slack-config', + templateUrl: './slack-config.component.html', + styleUrls: ['./slack-config.component.scss'] +}) +export class SlackConfigComponent extends RuleNodeConfigurationComponent { + + slackConfigForm: FormGroup; + slackChanelTypes = Object.keys(SlackChanelType) as SlackChanelType[]; + slackChanelTypesTranslateMap = SlackChanelTypesTranslateMap; + + constructor(private fb: FormBuilder) { + super(); + } + + protected configForm(): FormGroup { + return this.slackConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.slackConfigForm = this.fb.group({ + botToken: [configuration ? configuration.botToken : null], + useSystemSettings: [configuration ? configuration.useSystemSettings : false], + messageTemplate: [configuration ? configuration.messageTemplate : null, [Validators.required]], + conversationType: [configuration ? configuration.conversationType : null, [Validators.required]], + conversation: [configuration ? configuration.conversation : null, [Validators.required]], + }); + } + + protected validatorTriggers(): string[] { + return ['useSystemSettings']; + } + + protected updateValidators(emitEvent: boolean) { + const useSystemSettings: boolean = this.slackConfigForm.get('useSystemSettings').value; + if (useSystemSettings) { + this.slackConfigForm.get('botToken').clearValidators(); + } else { + this.slackConfigForm.get('botToken').setValidators([Validators.required]); + } + this.slackConfigForm.get('botToken').updateValueAndValidity({emitEvent}); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/sns-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/sns-config.component.html new file mode 100644 index 0000000000..f4e4aa738d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/sns-config.component.html @@ -0,0 +1,48 @@ + +
    + + tb.rulenode.topic-arn-pattern + + + {{ 'tb.rulenode.topic-arn-pattern-required' | translate }} + + tb.rulenode.general-pattern-hint + + + tb.rulenode.aws-access-key-id + + + {{ 'tb.rulenode.aws-access-key-id-required' | translate }} + + + + tb.rulenode.aws-secret-access-key + + + {{ 'tb.rulenode.aws-secret-access-key-required' | translate }} + + + + tb.rulenode.aws-region + + + {{ 'tb.rulenode.aws-region-required' | translate }} + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/sns-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/external/sns-config.component.ts new file mode 100644 index 0000000000..9c2159c2a9 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/sns-config.component.ts @@ -0,0 +1,46 @@ +/// +/// 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-external-node-sns-config', + templateUrl: './sns-config.component.html', + styleUrls: [] +}) +export class SnsConfigComponent extends RuleNodeConfigurationComponent { + + snsConfigForm: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.snsConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.snsConfigForm = this.fb.group({ + topicArnPattern: [configuration ? configuration.topicArnPattern : null, [Validators.required]], + accessKeyId: [configuration ? configuration.accessKeyId : null, [Validators.required]], + secretAccessKey: [configuration ? configuration.secretAccessKey : null, [Validators.required]], + region: [configuration ? configuration.region : null, [Validators.required]] + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/sqs-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/sqs-config.component.html new file mode 100644 index 0000000000..a7272e6d66 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/sqs-config.component.html @@ -0,0 +1,76 @@ + +
    + + tb.rulenode.queue-type + + + {{ sqsQueueTypeTranslationsMap.get(type) | translate }} + + + + + tb.rulenode.queue-url-pattern + + + {{ 'tb.rulenode.queue-url-pattern-required' | translate }} + + tb.rulenode.general-pattern-hint + + + tb.rulenode.delay-seconds + + + {{ 'tb.rulenode.min-delay-seconds-message' | translate }} + + + {{ 'tb.rulenode.max-delay-seconds-message' | translate }} + + + +
    + + + + tb.rulenode.aws-access-key-id + + + {{ 'tb.rulenode.aws-access-key-id-required' | translate }} + + + + tb.rulenode.aws-secret-access-key + + + {{ 'tb.rulenode.aws-secret-access-key-required' | translate }} + + + + tb.rulenode.aws-region + + + {{ 'tb.rulenode.aws-region-required' | translate }} + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/sqs-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/external/sqs-config.component.ts new file mode 100644 index 0000000000..8e77682c4f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/sqs-config.component.ts @@ -0,0 +1,54 @@ +/// +/// 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 { SqsQueueType, sqsQueueTypeTranslations } from '@home/components/rule-node/rule-node-config.models'; + +@Component({ + selector: 'tb-external-node-sqs-config', + templateUrl: './sqs-config.component.html', + styleUrls: [] +}) +export class SqsConfigComponent extends RuleNodeConfigurationComponent { + + sqsConfigForm: UntypedFormGroup; + + sqsQueueType = SqsQueueType; + sqsQueueTypes = Object.keys(SqsQueueType); + sqsQueueTypeTranslationsMap = sqsQueueTypeTranslations; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.sqsConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.sqsConfigForm = this.fb.group({ + queueType: [configuration ? configuration.queueType : null, [Validators.required]], + queueUrlPattern: [configuration ? configuration.queueUrlPattern : null, [Validators.required]], + delaySeconds: [configuration ? configuration.delaySeconds : null, [Validators.min(0), Validators.max(900)]], + messageAttributes: [configuration ? configuration.messageAttributes : null, []], + accessKeyId: [configuration ? configuration.accessKeyId : null, [Validators.required]], + secretAccessKey: [configuration ? configuration.secretAccessKey : null, [Validators.required]], + region: [configuration ? configuration.region : null, [Validators.required]] + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/check-alarm-status.component.html b/ui-ngx/src/app/modules/home/components/rule-node/filter/check-alarm-status.component.html new file mode 100644 index 0000000000..329b876e1e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/check-alarm-status.component.html @@ -0,0 +1,29 @@ + +
    +
    +
    tb.rulenode.alarm-status
    +
    + tb.rulenode.alarm-required +
    +
    + +
    + + + diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/check-alarm-status.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/filter/check-alarm-status.component.ts new file mode 100644 index 0000000000..125fde1bd6 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/check-alarm-status.component.ts @@ -0,0 +1,52 @@ +/// +/// 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 { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models'; + +@Component({ + selector: 'tb-filter-node-check-alarm-status-config', + templateUrl: './check-alarm-status.component.html', + styleUrls: [] +}) +export class CheckAlarmStatusComponent extends RuleNodeConfigurationComponent { + alarmStatusConfigForm: FormGroup; + + searchText = ''; + + constructor(private fb: FormBuilder) { + super(); + } + + protected configForm(): FormGroup { + return this.alarmStatusConfigForm; + } + + protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + return { + alarmStatusList: isDefinedAndNotNull(configuration?.alarmStatusList) ? configuration.alarmStatusList : null + }; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.alarmStatusConfigForm = this.fb.group({ + alarmStatusList: [configuration.alarmStatusList, [Validators.required]], + }); + } +} + diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/check-message-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/filter/check-message-config.component.html new file mode 100644 index 0000000000..7ded71d798 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/check-message-config.component.html @@ -0,0 +1,45 @@ + +
    +
    +
    tb.rulenode.fields-to-check
    +
    + tb.rulenode.at-least-one-field-required +
    +
    + + help + + + help + +
    + + {{ 'tb.rulenode.check-all-keys' | translate }} + +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/check-message-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/filter/check-message-config.component.ts new file mode 100644 index 0000000000..2904c8eee2 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/check-message-config.component.ts @@ -0,0 +1,78 @@ +/// +/// 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, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; +import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models'; + +@Component({ + selector: 'tb-filter-node-check-message-config', + templateUrl: './check-message-config.component.html', + styleUrls: [] +}) +export class CheckMessageConfigComponent extends RuleNodeConfigurationComponent { + + checkMessageConfigForm: FormGroup; + + constructor(private fb: FormBuilder) { + super(); + } + + protected configForm(): FormGroup { + return this.checkMessageConfigForm; + } + + protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + return { + messageNames: isDefinedAndNotNull(configuration?.messageNames) ? configuration.messageNames : [], + metadataNames: isDefinedAndNotNull(configuration?.metadataNames) ? configuration.metadataNames : [], + checkAllKeys: isDefinedAndNotNull(configuration?.checkAllKeys) ? configuration.checkAllKeys : false + }; + } + + protected prepareOutputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + return { + messageNames: isDefinedAndNotNull(configuration?.messageNames) ? configuration.messageNames : [], + metadataNames: isDefinedAndNotNull(configuration?.metadataNames) ? configuration.metadataNames : [], + checkAllKeys: configuration.checkAllKeys + }; + } + + + private atLeastOne(validator: ValidatorFn, controls: string[] = null) { + return (group: FormGroup): ValidationErrors | null => { + if (!controls) { + controls = Object.keys(group.controls); + } + const hasAtLeastOne = group?.controls && controls.some(k => !validator(group.controls[k])); + + return hasAtLeastOne ? null : {atLeastOne: true}; + }; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.checkMessageConfigForm = this.fb.group({ + messageNames: [configuration.messageNames, []], + metadataNames: [configuration.metadataNames, []], + checkAllKeys: [configuration.checkAllKeys, []] + }, {validators: this.atLeastOne(Validators.required, ['messageNames', 'metadataNames'])}); + } + + get touchedValidationControl(): boolean { + return ['messageNames', 'metadataNames'].some(name => this.checkMessageConfigForm.get(name).touched); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/check-relation-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/filter/check-relation-config.component.html new file mode 100644 index 0000000000..2a4b61792e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/check-relation-config.component.html @@ -0,0 +1,55 @@ + +
    +
    tb.rulenode.relation-search-parameters
    +
    + + {{ 'relation.direction' | translate }} + + + {{ entitySearchDirectionTranslationsMap.get(direction) | translate }} tb.rulenode.relations-query-config-direction-suffix + + + + + +
    + + {{ 'tb.rulenode.check-relation-to-specific-entity' | translate }} + +
    +
    + + + + +
    +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/check-relation-config.component.scss b/ui-ngx/src/app/modules/home/components/rule-node/filter/check-relation-config.component.scss new file mode 100644 index 0000000000..b61d0a5bb5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/check-relation-config.component.scss @@ -0,0 +1,20 @@ +/** + * 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 { + .slide-toggle { + margin-bottom: 18px + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/check-relation-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/filter/check-relation-config.component.ts new file mode 100644 index 0000000000..6c8a01050f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/check-relation-config.component.ts @@ -0,0 +1,77 @@ +/// +/// 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 { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { EntitySearchDirection, entitySearchDirectionTranslations } from '@app/shared/models/relation.models'; +import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models'; + +@Component({ + selector: 'tb-filter-node-check-relation-config', + templateUrl: './check-relation-config.component.html', + styleUrls: ['./check-relation-config.component.scss'] +}) +export class CheckRelationConfigComponent extends RuleNodeConfigurationComponent { + + checkRelationConfigForm: UntypedFormGroup; + + entitySearchDirection: Array = Object.values(EntitySearchDirection); + entitySearchDirectionTranslationsMap = entitySearchDirectionTranslations; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.checkRelationConfigForm; + } + + protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + return { + checkForSingleEntity: isDefinedAndNotNull(configuration?.checkForSingleEntity) ? configuration.checkForSingleEntity : false, + direction: isDefinedAndNotNull(configuration?.direction) ? configuration.direction : null, + entityType: isDefinedAndNotNull(configuration?.entityType) ? configuration.entityType : null, + entityId: isDefinedAndNotNull(configuration?.entityId) ? configuration.entityId : null, + relationType: isDefinedAndNotNull(configuration?.relationType) ? configuration.relationType : null + }; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.checkRelationConfigForm = this.fb.group({ + checkForSingleEntity: [configuration.checkForSingleEntity, []], + direction: [configuration.direction, []], + entityType: [configuration.entityType, + configuration && configuration.checkForSingleEntity ? [Validators.required] : []], + entityId: [configuration.entityId, + configuration && configuration.checkForSingleEntity ? [Validators.required] : []], + relationType: [configuration.relationType, [Validators.required]] + }); + } + + protected validatorTriggers(): string[] { + return ['checkForSingleEntity']; + } + + protected updateValidators(emitEvent: boolean) { + const checkForSingleEntity: boolean = this.checkRelationConfigForm.get('checkForSingleEntity').value; + this.checkRelationConfigForm.get('entityType').setValidators(checkForSingleEntity ? [Validators.required] : []); + this.checkRelationConfigForm.get('entityType').updateValueAndValidity({emitEvent}); + this.checkRelationConfigForm.get('entityId').setValidators(checkForSingleEntity ? [Validators.required] : []); + this.checkRelationConfigForm.get('entityId').updateValueAndValidity({emitEvent}); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/gps-geo-filter-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/filter/gps-geo-filter-config.component.html new file mode 100644 index 0000000000..738e834450 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/gps-geo-filter-config.component.html @@ -0,0 +1,126 @@ + +
    +
    +
    tb.rulenode.coordinate-field-names
    +
    +
    + + {{ 'tb.rulenode.latitude-field-name' | translate }} + + + {{ 'tb.rulenode.latitude-field-name-required' | translate }} + + + + {{ 'tb.rulenode.longitude-field-name' | translate }} + + + {{ 'tb.rulenode.longitude-field-name-required' | translate }} + + +
    +
    tb.rulenode.coordinate-field-hint
    +
    +
    +
    +
    tb.rulenode.geofence-configuration
    +
    + + {{ 'tb.rulenode.perimeter-type' | translate }} + + + {{ perimeterTypeTranslationMap.get(type) | translate }} + + + +
    + + {{ 'tb.rulenode.fetch-perimeter-info-from-metadata' | translate }} + +
    + + {{ 'tb.rulenode.perimeter-key-name' | translate }} + + + {{ 'tb.rulenode.perimeter-key-name-required' | translate }} + + {{ 'tb.rulenode.perimeter-key-name-hint' | translate }} + +
    +
    + + {{ 'tb.rulenode.circle-center-latitude' | translate }} + + + {{ 'tb.rulenode.circle-center-latitude-required' | translate }} + + + + {{ 'tb.rulenode.circle-center-longitude' | translate }} + + + {{ 'tb.rulenode.circle-center-longitude-required' | translate }} + + +
    +
    + + {{ 'tb.rulenode.range' | translate }} + + + {{ 'tb.rulenode.range-required' | translate }} + + + + {{ 'tb.rulenode.range-units' | translate }} + + + {{ rangeUnitTranslationMap.get(type) | translate }} + + + + {{ 'tb.rulenode.range-units-required' | translate }} + + +
    +
    + + {{ 'tb.rulenode.polygon-definition' | translate }} + + {{ 'tb.rulenode.polygon-definition-hint' | translate }} + + {{ 'tb.rulenode.polygon-definition-required' | translate }} + + +
    +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/gps-geo-filter-config.component.scss b/ui-ngx/src/app/modules/home/components/rule-node/filter/gps-geo-filter-config.component.scss new file mode 100644 index 0000000000..f177555e37 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/gps-geo-filter-config.component.scss @@ -0,0 +1,20 @@ +/** + * 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 { + .slide-toggle { + margin-bottom: 18px; + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/gps-geo-filter-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/filter/gps-geo-filter-config.component.ts new file mode 100644 index 0000000000..6fb2158e7a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/gps-geo-filter-config.component.ts @@ -0,0 +1,121 @@ +/// +/// 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 { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models'; +import { PerimeterType, perimeterTypeTranslations, RangeUnit, rangeUnitTranslations } from '../rule-node-config.models'; + +@Component({ + selector: 'tb-filter-node-gps-geofencing-config', + templateUrl: './gps-geo-filter-config.component.html', + styleUrls: ['./gps-geo-filter-config.component.scss'] +}) +export class GpsGeoFilterConfigComponent extends RuleNodeConfigurationComponent { + + geoFilterConfigForm: FormGroup; + + perimeterType = PerimeterType; + perimeterTypes: Array = Object.values(PerimeterType); + perimeterTypeTranslationMap = perimeterTypeTranslations; + + rangeUnits: Array = Object.values(RangeUnit); + rangeUnitTranslationMap = rangeUnitTranslations; + + public defaultPaddingEnable = true; + + constructor(private fb: FormBuilder) { + super(); + } + + protected configForm(): FormGroup { + return this.geoFilterConfigForm; + } + + protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + return { + latitudeKeyName: isDefinedAndNotNull(configuration?.latitudeKeyName) ? configuration.latitudeKeyName : null, + longitudeKeyName: isDefinedAndNotNull(configuration?.longitudeKeyName) ? configuration.longitudeKeyName : null, + perimeterType: isDefinedAndNotNull(configuration?.perimeterType) ? configuration.perimeterType : null, + fetchPerimeterInfoFromMessageMetadata: isDefinedAndNotNull(configuration?.fetchPerimeterInfoFromMessageMetadata) ? + configuration.fetchPerimeterInfoFromMessageMetadata : false, + perimeterKeyName: isDefinedAndNotNull(configuration?.perimeterKeyName) ? configuration.perimeterKeyName : null, + centerLatitude: isDefinedAndNotNull(configuration?.centerLatitude) ? configuration.centerLatitude : null, + centerLongitude: isDefinedAndNotNull(configuration?.centerLongitude) ? configuration.centerLongitude : null, + range: isDefinedAndNotNull(configuration?.range) ? configuration.range : null, + rangeUnit: isDefinedAndNotNull(configuration?.rangeUnit) ? configuration.rangeUnit : null, + polygonsDefinition: isDefinedAndNotNull(configuration?.polygonsDefinition) ? configuration.polygonsDefinition : null + }; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.geoFilterConfigForm = this.fb.group({ + latitudeKeyName: [configuration.latitudeKeyName, [Validators.required]], + longitudeKeyName: [configuration.longitudeKeyName, [Validators.required]], + perimeterType: [configuration.perimeterType, [Validators.required]], + fetchPerimeterInfoFromMessageMetadata: [configuration.fetchPerimeterInfoFromMessageMetadata, []], + perimeterKeyName: [configuration.perimeterKeyName, []], + centerLatitude: [configuration.centerLatitude, []], + centerLongitude: [configuration.centerLongitude, []], + range: [configuration.range, []], + rangeUnit: [configuration.rangeUnit, []], + polygonsDefinition: [configuration.polygonsDefinition, []] + }); + } + + protected validatorTriggers(): string[] { + return ['fetchPerimeterInfoFromMessageMetadata', 'perimeterType']; + } + + protected updateValidators(emitEvent: boolean) { + const fetchPerimeterInfoFromMessageMetadata: boolean = this.geoFilterConfigForm.get('fetchPerimeterInfoFromMessageMetadata').value; + const perimeterType: PerimeterType = this.geoFilterConfigForm.get('perimeterType').value; + if (fetchPerimeterInfoFromMessageMetadata) { + this.geoFilterConfigForm.get('perimeterKeyName').setValidators([Validators.required]); + } else { + this.geoFilterConfigForm.get('perimeterKeyName').setValidators([]); + } + if (!fetchPerimeterInfoFromMessageMetadata && perimeterType === PerimeterType.CIRCLE) { + this.geoFilterConfigForm.get('centerLatitude').setValidators([Validators.required, + Validators.min(-90), Validators.max(90)]); + this.geoFilterConfigForm.get('centerLongitude').setValidators([Validators.required, + Validators.min(-180), Validators.max(180)]); + this.geoFilterConfigForm.get('range').setValidators([Validators.required, Validators.min(0)]); + this.geoFilterConfigForm.get('rangeUnit').setValidators([Validators.required]); + + this.defaultPaddingEnable = false; + } else { + this.geoFilterConfigForm.get('centerLatitude').setValidators([]); + this.geoFilterConfigForm.get('centerLongitude').setValidators([]); + this.geoFilterConfigForm.get('range').setValidators([]); + this.geoFilterConfigForm.get('rangeUnit').setValidators([]); + + this.defaultPaddingEnable = true; + } + if (!fetchPerimeterInfoFromMessageMetadata && perimeterType === PerimeterType.POLYGON) { + this.geoFilterConfigForm.get('polygonsDefinition').setValidators([Validators.required]); + } else { + this.geoFilterConfigForm.get('polygonsDefinition').setValidators([]); + } + this.geoFilterConfigForm.get('perimeterKeyName').updateValueAndValidity({emitEvent}); + this.geoFilterConfigForm.get('centerLatitude').updateValueAndValidity({emitEvent}); + this.geoFilterConfigForm.get('centerLongitude').updateValueAndValidity({emitEvent}); + this.geoFilterConfigForm.get('range').updateValueAndValidity({emitEvent}); + this.geoFilterConfigForm.get('rangeUnit').updateValueAndValidity({emitEvent}); + this.geoFilterConfigForm.get('polygonsDefinition').updateValueAndValidity({emitEvent}); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/message-type-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/filter/message-type-config.component.html new file mode 100644 index 0000000000..1b347ca1d1 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/message-type-config.component.html @@ -0,0 +1,24 @@ + +
    + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/message-type-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/filter/message-type-config.component.ts new file mode 100644 index 0000000000..0f8392c4f0 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/message-type-config.component.ts @@ -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. +/// + +import { Component } from '@angular/core'; +import { AppState, isDefinedAndNotNull } from '@core/public-api'; +import { Store } from '@ngrx/store'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; + +@Component({ + selector: 'tb-filter-node-message-type-config', + templateUrl: './message-type-config.component.html', + styleUrls: [] +}) +export class MessageTypeConfigComponent extends RuleNodeConfigurationComponent { + + messageTypeConfigForm: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.messageTypeConfigForm; + } + + protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + return { + messageTypes: isDefinedAndNotNull(configuration?.messageTypes) ? configuration.messageTypes : null + }; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.messageTypeConfigForm = this.fb.group({ + messageTypes: [configuration.messageTypes, [Validators.required]] + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/originator-type-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/filter/originator-type-config.component.html new file mode 100644 index 0000000000..233106bd8e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/originator-type-config.component.html @@ -0,0 +1,31 @@ + +
    + + help + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/originator-type-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/filter/originator-type-config.component.ts new file mode 100644 index 0000000000..91e945ee0f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/originator-type-config.component.ts @@ -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 { isDefinedAndNotNull } from '@core/public-api'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models'; +import { EntityType } from '@app/shared/models/entity-type.models'; + +@Component({ + selector: 'tb-filter-node-originator-type-config', + templateUrl: './originator-type-config.component.html', + styleUrls: [] +}) +export class OriginatorTypeConfigComponent extends RuleNodeConfigurationComponent { + + originatorTypeConfigForm: UntypedFormGroup; + + allowedEntityTypes: EntityType[] = [ + EntityType.DEVICE, + EntityType.ASSET, + EntityType.ENTITY_VIEW, + EntityType.TENANT, + EntityType.CUSTOMER, + EntityType.USER, + EntityType.DASHBOARD, + EntityType.RULE_CHAIN, + EntityType.RULE_NODE, + EntityType.EDGE + ]; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.originatorTypeConfigForm; + } + + protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + return { + originatorTypes: isDefinedAndNotNull(configuration?.originatorTypes) ? configuration.originatorTypes : null + }; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.originatorTypeConfigForm = this.fb.group({ + originatorTypes: [configuration.originatorTypes, [Validators.required]] + }); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/rule-node-config-filter.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/filter/rule-node-config-filter.module.ts new file mode 100644 index 0000000000..52ad3169d5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/rule-node-config-filter.module.ts @@ -0,0 +1,69 @@ +/// +/// 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, Type } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { IRuleNodeConfigurationComponent, SharedModule } from '@shared/public-api'; +import { CheckMessageConfigComponent } from './check-message-config.component'; +import { CheckRelationConfigComponent } from './check-relation-config.component'; +import { GpsGeoFilterConfigComponent } from './gps-geo-filter-config.component'; +import { MessageTypeConfigComponent } from './message-type-config.component'; +import { OriginatorTypeConfigComponent } from './originator-type-config.component'; +import { ScriptConfigComponent } from './script-config.component'; +import { SwitchConfigComponent } from './switch-config.component'; +import { CheckAlarmStatusComponent } from './check-alarm-status.component'; +import { RuleNodeConfigCommonModule } from '../common/rule-node-config-common.module'; + +@NgModule({ + declarations: [ + CheckMessageConfigComponent, + CheckRelationConfigComponent, + GpsGeoFilterConfigComponent, + MessageTypeConfigComponent, + OriginatorTypeConfigComponent, + ScriptConfigComponent, + SwitchConfigComponent, + CheckAlarmStatusComponent + ], + imports: [ + CommonModule, + SharedModule, + RuleNodeConfigCommonModule + ], + exports: [ + CheckMessageConfigComponent, + CheckRelationConfigComponent, + GpsGeoFilterConfigComponent, + MessageTypeConfigComponent, + OriginatorTypeConfigComponent, + ScriptConfigComponent, + SwitchConfigComponent, + CheckAlarmStatusComponent + ] +}) +export class RuleNodeConfigFilterModule { +} + +export const ruleNodeFilterConfigComponentsMap: Record> = { + 'tbFilterNodeCheckAlarmStatusConfig': CheckAlarmStatusComponent, + 'tbFilterNodeCheckMessageConfig': CheckMessageConfigComponent, + 'tbFilterNodeCheckRelationConfig': CheckRelationConfigComponent, + 'tbFilterNodeGpsGeofencingConfig': GpsGeoFilterConfigComponent, + 'tbFilterNodeMessageTypeConfig': MessageTypeConfigComponent, + 'tbFilterNodeOriginatorTypeConfig': OriginatorTypeConfigComponent, + 'tbFilterNodeScriptConfig': ScriptConfigComponent, + 'tbFilterNodeSwitchConfig': SwitchConfigComponent +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/script-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/filter/script-config.component.html new file mode 100644 index 0000000000..c0b39f77b7 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/script-config.component.html @@ -0,0 +1,57 @@ + +
    + + + + + + + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/script-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/filter/script-config.component.ts new file mode 100644 index 0000000000..643f66448f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/script-config.component.ts @@ -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. +/// + +import { Component, EventEmitter, ViewChild } from '@angular/core'; +import { AppState, getCurrentAuthState, isDefinedAndNotNull, 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 '@shared/models/rule-node.models'; +import type { JsFuncComponent } from '@app/shared/components/js-func.component'; +import { DebugRuleNodeEventBody } from '@app/shared/models/event.models'; + +@Component({ + selector: 'tb-filter-node-script-config', + templateUrl: './script-config.component.html', + styleUrls: [] +}) +export class ScriptConfigComponent extends RuleNodeConfigurationComponent { + + @ViewChild('jsFuncComponent', {static: false}) jsFuncComponent: JsFuncComponent; + @ViewChild('tbelFuncComponent', {static: false}) tbelFuncComponent: JsFuncComponent; + + scriptConfigForm: UntypedFormGroup; + + tbelEnabled = getCurrentAuthState(this.store).tbelEnabled; + + scriptLanguage = ScriptLanguage; + + changeScript: EventEmitter = new EventEmitter(); + + readonly hasScript = true; + + readonly testScriptLabel = 'tb.rulenode.test-filter-function'; + + constructor(protected store: Store, + private fb: UntypedFormBuilder, + private nodeScriptTestService: NodeScriptTestService, + private translate: TranslateService) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.scriptConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.scriptConfigForm = this.fb.group({ + scriptLang: [configuration.scriptLang, [Validators.required]], + jsScript: [configuration.jsScript, []], + tbelScript: [configuration.tbelScript, []] + }); + } + + protected validatorTriggers(): string[] { + return ['scriptLang']; + } + + protected updateValidators(emitEvent: boolean) { + let scriptLang: ScriptLanguage = this.scriptConfigForm.get('scriptLang').value; + if (scriptLang === ScriptLanguage.TBEL && !this.tbelEnabled) { + scriptLang = ScriptLanguage.JS; + this.scriptConfigForm.get('scriptLang').patchValue(scriptLang, {emitEvent: false}); + setTimeout(() => { + this.scriptConfigForm.updateValueAndValidity({emitEvent: true}); + }); + } + this.scriptConfigForm.get('jsScript').setValidators(scriptLang === ScriptLanguage.JS ? [Validators.required] : []); + this.scriptConfigForm.get('jsScript').updateValueAndValidity({emitEvent}); + this.scriptConfigForm.get('tbelScript').setValidators(scriptLang === ScriptLanguage.TBEL ? [Validators.required] : []); + this.scriptConfigForm.get('tbelScript').updateValueAndValidity({emitEvent}); + } + + protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + if (configuration) { + if (!configuration.scriptLang) { + configuration.scriptLang = ScriptLanguage.JS; + } + } + return { + scriptLang: isDefinedAndNotNull(configuration?.scriptLang) ? configuration.scriptLang : ScriptLanguage.JS, + jsScript: isDefinedAndNotNull(configuration?.jsScript) ? configuration.jsScript : null, + tbelScript: isDefinedAndNotNull(configuration?.tbelScript) ? configuration.tbelScript : null + }; + } + + testScript(debugEventBody?: DebugRuleNodeEventBody) { + const scriptLang: ScriptLanguage = this.scriptConfigForm.get('scriptLang').value; + const scriptField = scriptLang === ScriptLanguage.JS ? 'jsScript' : 'tbelScript'; + const helpId = scriptLang === ScriptLanguage.JS ? 'rulenode/filter_node_script_fn' : 'rulenode/tbel/filter_node_script_fn'; + const script: string = this.scriptConfigForm.get(scriptField).value; + this.nodeScriptTestService.testNodeScript( + script, + 'filter', + this.translate.instant('tb.rulenode.filter'), + 'Filter', + ['msg', 'metadata', 'msgType'], + this.ruleNodeId, + helpId, + scriptLang, + debugEventBody + ).subscribe((theScript) => { + if (theScript) { + this.scriptConfigForm.get(scriptField).setValue(theScript); + this.changeScript.emit(); + } + }); + } + + protected onValidate() { + const scriptLang: ScriptLanguage = this.scriptConfigForm.get('scriptLang').value; + if (scriptLang === ScriptLanguage.JS) { + this.jsFuncComponent.validateOnSubmit(); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/switch-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/filter/switch-config.component.html new file mode 100644 index 0000000000..83034e2e43 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/switch-config.component.html @@ -0,0 +1,57 @@ + +
    + + + + + + + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/switch-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/filter/switch-config.component.ts new file mode 100644 index 0000000000..a77e5d3e29 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/switch-config.component.ts @@ -0,0 +1,130 @@ +/// +/// 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 { DebugRuleNodeEventBody } from '@shared/models/event.models'; + +@Component({ + selector: 'tb-filter-node-switch-config', + templateUrl: './switch-config.component.html', + styleUrls: [] +}) +export class SwitchConfigComponent extends RuleNodeConfigurationComponent { + + @ViewChild('jsFuncComponent', {static: false}) jsFuncComponent: JsFuncComponent; + @ViewChild('tbelFuncComponent', {static: false}) tbelFuncComponent: JsFuncComponent; + + switchConfigForm: UntypedFormGroup; + + tbelEnabled = getCurrentAuthState(this.store).tbelEnabled; + + scriptLanguage = ScriptLanguage; + + changeScript: EventEmitter = new EventEmitter(); + + readonly hasScript = true; + + readonly testScriptLabel = 'tb.rulenode.test-switch-function'; + + constructor(private fb: UntypedFormBuilder, + private nodeScriptTestService: NodeScriptTestService, + private translate: TranslateService) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.switchConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.switchConfigForm = this.fb.group({ + scriptLang: [configuration.scriptLang, [Validators.required]], + jsScript: [configuration.jsScript, []], + tbelScript: [configuration.tbelScript, []] + }); + } + + protected validatorTriggers(): string[] { + return ['scriptLang']; + } + + protected updateValidators(emitEvent: boolean) { + let scriptLang: ScriptLanguage = this.switchConfigForm.get('scriptLang').value; + if (scriptLang === ScriptLanguage.TBEL && !this.tbelEnabled) { + scriptLang = ScriptLanguage.JS; + this.switchConfigForm.get('scriptLang').patchValue(scriptLang, {emitEvent: false}); + setTimeout(() => { + this.switchConfigForm.updateValueAndValidity({emitEvent: true}); + }); + } + this.switchConfigForm.get('jsScript').setValidators(scriptLang === ScriptLanguage.JS ? [Validators.required] : []); + this.switchConfigForm.get('jsScript').updateValueAndValidity({emitEvent}); + this.switchConfigForm.get('tbelScript').setValidators(scriptLang === ScriptLanguage.TBEL ? [Validators.required] : []); + this.switchConfigForm.get('tbelScript').updateValueAndValidity({emitEvent}); + } + + protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + if (configuration) { + if (!configuration.scriptLang) { + configuration.scriptLang = ScriptLanguage.JS; + } + } + return { + scriptLang: isDefinedAndNotNull(configuration?.scriptLang) ? configuration.scriptLang : ScriptLanguage.JS, + jsScript: isDefinedAndNotNull(configuration?.jsScript) ? configuration.jsScript : null, + tbelScript: isDefinedAndNotNull(configuration?.tbelScript) ? configuration.tbelScript : null + }; + } + + testScript(debugEventBody?: DebugRuleNodeEventBody) { + const scriptLang: ScriptLanguage = this.switchConfigForm.get('scriptLang').value; + const scriptField = scriptLang === ScriptLanguage.JS ? 'jsScript' : 'tbelScript'; + const helpId = scriptLang === ScriptLanguage.JS ? 'rulenode/switch_node_script_fn' : 'rulenode/tbel/switch_node_script_fn'; + const script: string = this.switchConfigForm.get(scriptField).value; + this.nodeScriptTestService.testNodeScript( + script, + 'switch', + this.translate.instant('tb.rulenode.switch'), + 'Switch', + ['msg', 'metadata', 'msgType'], + this.ruleNodeId, + helpId, + scriptLang, + debugEventBody + ).subscribe((theScript) => { + if (theScript) { + this.switchConfigForm.get(scriptField).setValue(theScript); + this.changeScript.emit(); + } + }); + } + + protected onValidate() { + const scriptLang: ScriptLanguage = this.switchConfigForm.get('scriptLang').value; + if (scriptLang === ScriptLanguage.JS) { + this.jsFuncComponent.validateOnSubmit(); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/flow/rule-chain-input.component.html b/ui-ngx/src/app/modules/home/components/rule-node/flow/rule-chain-input.component.html new file mode 100644 index 0000000000..a91b9980d4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/flow/rule-chain-input.component.html @@ -0,0 +1,33 @@ + +
    +
    +
    + + {{ 'tb.rulenode.forward-msg-default-rule-chain' | translate }} + +
    + + +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/flow/rule-chain-input.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/flow/rule-chain-input.component.ts new file mode 100644 index 0000000000..9e5316841a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/flow/rule-chain-input.component.ts @@ -0,0 +1,48 @@ +/// +/// 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 { EntityType } from '@shared/models/entity-type.models'; + +@Component({ + selector: 'tb-flow-node-rule-chain-input-config', + templateUrl: './rule-chain-input.component.html', + styleUrls: [] +}) +export class RuleChainInputComponent extends RuleNodeConfigurationComponent { + + entityType = EntityType; + + ruleChainInputConfigForm: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.ruleChainInputConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.ruleChainInputConfigForm = this.fb.group({ + forwardMsgToDefaultRuleChain: [configuration ? configuration?.forwardMsgToDefaultRuleChain : false, []], + ruleChainId: [configuration ? configuration.ruleChainId : null, [Validators.required]] + }); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/flow/rule-chain-output.component.html b/ui-ngx/src/app/modules/home/components/rule-node/flow/rule-chain-output.component.html new file mode 100644 index 0000000000..d5c039c030 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/flow/rule-chain-output.component.html @@ -0,0 +1,20 @@ + +
    +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/flow/rule-chain-output.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/flow/rule-chain-output.component.ts new file mode 100644 index 0000000000..153272f374 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/flow/rule-chain-output.component.ts @@ -0,0 +1,42 @@ +/// +/// 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-flow-node-rule-chain-output-config', + templateUrl: './rule-chain-output.component.html', + styleUrls: [] +}) +export class RuleChainOutputComponent extends RuleNodeConfigurationComponent { + + ruleChainOutputConfigForm: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder) { + super(); + } + + protected configForm(): UntypedFormGroup { + return this.ruleChainOutputConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.ruleChainOutputConfigForm = this.fb.group({}); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/flow/rule-node-config-flow.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/flow/rule-node-config-flow.module.ts new file mode 100644 index 0000000000..4dae3212aa --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/flow/rule-node-config-flow.module.ts @@ -0,0 +1,43 @@ +/// +/// 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, Type } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { IRuleNodeConfigurationComponent, SharedModule } from '@shared/public-api'; +import { RuleChainInputComponent } from './rule-chain-input.component'; +import { RuleChainOutputComponent } from './rule-chain-output.component'; + +@NgModule({ + declarations: [ + RuleChainInputComponent, + RuleChainOutputComponent + ], + imports: [ + CommonModule, + SharedModule + ], + exports: [ + RuleChainInputComponent, + RuleChainOutputComponent + ] +}) +export class RuleNodeConfigFlowModule { +} + +export const ruleNodeFlowConfigComponentsMap: Record> = { + 'tbFlowNodeRuleChainInputConfig': RuleChainInputComponent, + 'tbFlowNodeRuleChainOutputConfig': RuleChainOutputComponent +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.models.ts b/ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.models.ts new file mode 100644 index 0000000000..6562a732aa --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.models.ts @@ -0,0 +1,862 @@ +/// +/// 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 { EntityField, entityFields } from '@shared/models/entity.models'; +import { EntitySearchDirection } from '@shared/models/relation.models'; +import { EntityTypeFilter } from '@shared/models/alias.models'; + +export enum OriginatorSource { + CUSTOMER = 'CUSTOMER', + TENANT = 'TENANT', + RELATED = 'RELATED', + ALARM_ORIGINATOR = 'ALARM_ORIGINATOR', + ENTITY = 'ENTITY' +} + +export interface OriginatorValuesDescriptions { + value: OriginatorSource; + name: string; + description: string; +} + +export const originatorSourceTranslations = new Map( + [ + [OriginatorSource.CUSTOMER, 'tb.rulenode.originator-customer'], + [OriginatorSource.TENANT, 'tb.rulenode.originator-tenant'], + [OriginatorSource.RELATED, 'tb.rulenode.originator-related'], + [OriginatorSource.ALARM_ORIGINATOR, 'tb.rulenode.originator-alarm-originator'], + [OriginatorSource.ENTITY, 'tb.rulenode.originator-entity'], + ] +); + +export const originatorSourceDescTranslations = new Map( + [ + [OriginatorSource.CUSTOMER, 'tb.rulenode.originator-customer-desc'], + [OriginatorSource.TENANT, 'tb.rulenode.originator-tenant-desc'], + [OriginatorSource.RELATED, 'tb.rulenode.originator-related-entity-desc'], + [OriginatorSource.ALARM_ORIGINATOR, 'tb.rulenode.originator-alarm-originator-desc'], + [OriginatorSource.ENTITY, 'tb.rulenode.originator-entity-by-name-pattern-desc'], + ] +); +export const allowedOriginatorFields: EntityField[] = [ + entityFields.createdTime, + entityFields.name, + {value: 'type', name: 'tb.rulenode.profile-name', keyName: 'originatorProfileName'}, + entityFields.firstName, + entityFields.lastName, + entityFields.email, + entityFields.title, + entityFields.country, + entityFields.state, + entityFields.city, + entityFields.address, + entityFields.address2, + entityFields.zip, + entityFields.phone, + entityFields.label, + {value: 'id', name: 'tb.rulenode.id', keyName: 'id'}, + {value: 'additionalInfo', name: 'tb.rulenode.additional-info', keyName: 'additionalInfo'} +]; + +export const OriginatorFieldsMappingValues = new Map( + [ + ['type', 'profileName'], + ['createdTime', 'createdTime'], + ['name', 'name'], + ['firstName', 'firstName'], + ['lastName', 'lastName'], + ['email', 'email'], + ['title', 'title'], + ['country', 'country'], + ['state', 'state'], + ['city', 'city'], + ['address', 'address'], + ['address2', 'address2'], + ['zip', 'zip'], + ['phone', 'phone'], + ['label', 'label'], + ['id', 'id'], + ['additionalInfo', 'additionalInfo'], + ] +); + +export enum PerimeterType { + CIRCLE = 'CIRCLE', + POLYGON = 'POLYGON' +} + +export const perimeterTypeTranslations = new Map( + [ + [PerimeterType.CIRCLE, 'tb.rulenode.perimeter-circle'], + [PerimeterType.POLYGON, 'tb.rulenode.perimeter-polygon'], + ] +); + +export enum TimeUnit { + MILLISECONDS = 'MILLISECONDS', + SECONDS = 'SECONDS', + MINUTES = 'MINUTES', + HOURS = 'HOURS', + DAYS = 'DAYS' +} + +export const timeUnitTranslations = new Map( + [ + [TimeUnit.MILLISECONDS, 'tb.rulenode.time-unit-milliseconds'], + [TimeUnit.SECONDS, 'tb.rulenode.time-unit-seconds'], + [TimeUnit.MINUTES, 'tb.rulenode.time-unit-minutes'], + [TimeUnit.HOURS, 'tb.rulenode.time-unit-hours'], + [TimeUnit.DAYS, 'tb.rulenode.time-unit-days'] + ] +); + +export enum RangeUnit { + METER = 'METER', + KILOMETER = 'KILOMETER', + FOOT = 'FOOT', + MILE = 'MILE', + NAUTICAL_MILE = 'NAUTICAL_MILE' +} + +export const rangeUnitTranslations = new Map( + [ + [RangeUnit.METER, 'tb.rulenode.range-unit-meter'], + [RangeUnit.KILOMETER, 'tb.rulenode.range-unit-kilometer'], + [RangeUnit.FOOT, 'tb.rulenode.range-unit-foot'], + [RangeUnit.MILE, 'tb.rulenode.range-unit-mile'], + [RangeUnit.NAUTICAL_MILE, 'tb.rulenode.range-unit-nautical-mile'] + ] +); + +export enum EntityDetailsField { + ID = 'ID', + TITLE = 'TITLE', + COUNTRY = 'COUNTRY', + STATE = 'STATE', + CITY = 'CITY', + ZIP = 'ZIP', + ADDRESS = 'ADDRESS', + ADDRESS2 = 'ADDRESS2', + PHONE = 'PHONE', + EMAIL = 'EMAIL', + ADDITIONAL_INFO = 'ADDITIONAL_INFO' +} + +export interface SvMapOption { + name: string; + value: any; +} + +export const entityDetailsTranslations = new Map( + [ + [EntityDetailsField.ID, 'tb.rulenode.entity-details-id'], + [EntityDetailsField.TITLE, 'tb.rulenode.entity-details-title'], + [EntityDetailsField.COUNTRY, 'tb.rulenode.entity-details-country'], + [EntityDetailsField.STATE, 'tb.rulenode.entity-details-state'], + [EntityDetailsField.CITY, 'tb.rulenode.entity-details-city'], + [EntityDetailsField.ZIP, 'tb.rulenode.entity-details-zip'], + [EntityDetailsField.ADDRESS, 'tb.rulenode.entity-details-address'], + [EntityDetailsField.ADDRESS2, 'tb.rulenode.entity-details-address2'], + [EntityDetailsField.PHONE, 'tb.rulenode.entity-details-phone'], + [EntityDetailsField.EMAIL, 'tb.rulenode.entity-details-email'], + [EntityDetailsField.ADDITIONAL_INFO, 'tb.rulenode.entity-details-additional_info'] + ] +); + +export enum FetchMode { + FIRST = 'FIRST', + LAST = 'LAST', + ALL = 'ALL' +} + +export const deduplicationStrategiesTranslations = new Map( + [ + [FetchMode.FIRST, 'tb.rulenode.first'], + [FetchMode.LAST, 'tb.rulenode.last'], + [FetchMode.ALL, 'tb.rulenode.all'] + ] +); + +export const deduplicationStrategiesHintTranslations = new Map( + [ + [FetchMode.FIRST, 'tb.rulenode.first-mode-hint'], + [FetchMode.LAST, 'tb.rulenode.last-mode-hint'], + [FetchMode.ALL, 'tb.rulenode.all-mode-hint'] + ] +); + +export enum SamplingOrder { + ASC = 'ASC', + DESC = 'DESC' +} + +export enum DataToFetch { + ATTRIBUTES = 'ATTRIBUTES', + LATEST_TELEMETRY = 'LATEST_TELEMETRY', + FIELDS = 'FIELDS' +} + +export const dataToFetchTranslations = new Map( + [ + [DataToFetch.ATTRIBUTES, 'tb.rulenode.attributes'], + [DataToFetch.LATEST_TELEMETRY, 'tb.rulenode.latest-telemetry'], + [DataToFetch.FIELDS, 'tb.rulenode.fields'] + ] +); + +export const msgMetadataLabelTranslations = new Map( + [ + [DataToFetch.ATTRIBUTES, 'tb.rulenode.add-mapped-attribute-to'], + [DataToFetch.LATEST_TELEMETRY, 'tb.rulenode.add-mapped-latest-telemetry-to'], + [DataToFetch.FIELDS, 'tb.rulenode.add-mapped-fields-to'] + ] +); + +export const samplingOrderTranslations = new Map( + [ + [SamplingOrder.ASC, 'tb.rulenode.ascending'], + [SamplingOrder.DESC, 'tb.rulenode.descending'] + ] +); + +export enum SqsQueueType { + STANDARD = 'STANDARD', + FIFO = 'FIFO' +} + +export const sqsQueueTypeTranslations = new Map( + [ + [SqsQueueType.STANDARD, 'tb.rulenode.sqs-queue-standard'], + [SqsQueueType.FIFO, 'tb.rulenode.sqs-queue-fifo'], + ] +); + +export type credentialsType = 'anonymous' | 'basic' | 'cert.PEM'; +export const credentialsTypes: credentialsType[] = ['anonymous', 'basic', 'cert.PEM']; + +export const credentialsTypeTranslations = new Map( + [ + ['anonymous', 'tb.rulenode.credentials-anonymous'], + ['basic', 'tb.rulenode.credentials-basic'], + ['cert.PEM', 'tb.rulenode.credentials-pem'] + ] +); + +export type AzureIotHubCredentialsType = 'sas' | 'cert.PEM'; +export const azureIotHubCredentialsTypes: AzureIotHubCredentialsType[] = ['sas', 'cert.PEM']; + +export const azureIotHubCredentialsTypeTranslations = new Map( + [ + ['sas', 'tb.rulenode.credentials-sas'], + ['cert.PEM', 'tb.rulenode.credentials-pem'] + ] +); + +export enum HttpRequestType { + GET = 'GET', + POST = 'POST', + PUT = 'PUT', + DELETE = 'DELETE' +} + +export const ToByteStandartCharsetTypes = [ + 'US-ASCII', + 'ISO-8859-1', + 'UTF-8', + 'UTF-16BE', + 'UTF-16LE', + 'UTF-16' +]; + +export const ToByteStandartCharsetTypeTranslations = new Map( + [ + ['US-ASCII', 'tb.rulenode.charset-us-ascii'], + ['ISO-8859-1', 'tb.rulenode.charset-iso-8859-1'], + ['UTF-8', 'tb.rulenode.charset-utf-8'], + ['UTF-16BE', 'tb.rulenode.charset-utf-16be'], + ['UTF-16LE', 'tb.rulenode.charset-utf-16le'], + ['UTF-16', 'tb.rulenode.charset-utf-16'], + ] +); + +export interface RelationsQuery { + fetchLastLevelOnly: boolean; + direction: EntitySearchDirection; + maxLevel?: number; + filters?: EntityTypeFilter[]; +} + +export interface FunctionData { + value: MathFunction; + name: string; + description: string; + minArgs: number; + maxArgs: number; +} + +export enum MathFunction { + CUSTOM = 'CUSTOM', + ADD = 'ADD', + SUB = 'SUB', + MULT = 'MULT', + DIV = 'DIV', + SIN = 'SIN', + SINH = 'SINH', + COS = 'COS', + COSH = 'COSH', + TAN = 'TAN', + TANH = 'TANH', + ACOS = 'ACOS', + ASIN = 'ASIN', + ATAN = 'ATAN', + ATAN2 = 'ATAN2', + EXP = 'EXP', + EXPM1 = 'EXPM1', + SQRT = 'SQRT', + CBRT = 'CBRT', + GET_EXP = 'GET_EXP', + HYPOT = 'HYPOT', + LOG = 'LOG', + LOG10 = 'LOG10', + LOG1P = 'LOG1P', + CEIL = 'CEIL', + FLOOR = 'FLOOR', + FLOOR_DIV = 'FLOOR_DIV', + FLOOR_MOD = 'FLOOR_MOD', + ABS = 'ABS', + MIN = 'MIN', + MAX = 'MAX', + POW = 'POW', + SIGNUM = 'SIGNUM', + RAD = 'RAD', + DEG = 'DEG', +} + +export const MathFunctionMap = new Map( + [ + [ + MathFunction.CUSTOM, + { + value: MathFunction.CUSTOM, + name: 'Custom Function', + description: 'Use this function to specify complex mathematical expression.', + minArgs: 1, + maxArgs: 16 + } + ], + [ + MathFunction.ADD, + { + value: MathFunction.ADD, + name: 'Addition', + description: 'x + y', + minArgs: 2, + maxArgs: 2 + } + ], + [ + MathFunction.SUB, + { + value: MathFunction.SUB, + name: 'Subtraction', + description: 'x - y', + minArgs: 2, + maxArgs: 2 + } + ], + [ + MathFunction.MULT, + { + value: MathFunction.MULT, + name: 'Multiplication', + description: 'x * y', + minArgs: 2, + maxArgs: 2 + } + ], + [ + MathFunction.DIV, + { + value: MathFunction.DIV, + name: 'Division', + description: 'x / y', + minArgs: 2, + maxArgs: 2 + } + ], + [ + MathFunction.SIN, + { + value: MathFunction.SIN, + name: 'Sine', + description: 'Returns the trigonometric sine of an angle in radians.', + minArgs: 1, + maxArgs: 1 + } + ], + [ + MathFunction.SINH, + { + value: MathFunction.SINH, + name: 'Hyperbolic sine', + description: 'Returns the hyperbolic sine of an argument.', + minArgs: 1, + maxArgs: 1 + } + ], + [ + MathFunction.COS, + { + value: MathFunction.COS, + name: 'Cosine', + description: 'Returns the trigonometric cosine of an angle in radians.', + minArgs: 1, + maxArgs: 1 + } + ], + [ + MathFunction.COSH, + { + value: MathFunction.COSH, + name: 'Hyperbolic cosine', + description: 'Returns the hyperbolic cosine of an argument.', + minArgs: 1, + maxArgs: 1 + } + ], + [ + MathFunction.TAN, + { + value: MathFunction.TAN, + name: 'Tangent', + description: 'Returns the trigonometric tangent of an angle in radians', + minArgs: 1, + maxArgs: 1 + } + ], + [ + MathFunction.TANH, + { + value: MathFunction.TANH, + name: 'Hyperbolic tangent', + description: 'Returns the hyperbolic tangent of an argument', + minArgs: 1, + maxArgs: 1 + } + ], + [ + MathFunction.ACOS, + { + value: MathFunction.ACOS, + name: 'Arc cosine', + description: 'Returns the arc cosine of an argument', + minArgs: 1, + maxArgs: 1 + } + ], + [ + MathFunction.ASIN, + { + value: MathFunction.ASIN, + name: 'Arc sine', + description: 'Returns the arc sine of an argument', + minArgs: 1, + maxArgs: 1 + } + ], + [ + MathFunction.ATAN, + { + value: MathFunction.ATAN, + name: 'Arc tangent', + description: 'Returns the arc tangent of an argument', + minArgs: 1, + maxArgs: 1 + } + ], + [ + MathFunction.ATAN2, + { + value: MathFunction.ATAN2, + name: '2-argument arc tangent', + description: 'Returns the angle theta from the conversion of rectangular coordinates (x, y) to polar coordinates (r, theta)', + minArgs: 2, + maxArgs: 2 + } + ], + [ + MathFunction.EXP, + { + value: MathFunction.EXP, + name: 'Exponential', + description: 'Returns Euler\'s number e raised to the power of an argument', + minArgs: 1, + maxArgs: 1 + } + ], + [ + MathFunction.EXPM1, + { + value: MathFunction.EXPM1, + name: 'Exponential minus one', + description: 'Returns Euler\'s number e raised to the power of an argument minus one', + minArgs: 1, + maxArgs: 1 + } + ], + [ + MathFunction.SQRT, + { + value: MathFunction.SQRT, + name: 'Square', + description: 'Returns the correctly rounded positive square root of an argument', + minArgs: 1, + maxArgs: 1 + } + ], + [ + MathFunction.CBRT, + { + value: MathFunction.CBRT, + name: 'Cube root', + description: 'Returns the cube root of an argument', + minArgs: 1, + maxArgs: 1 + } + ], + [ + MathFunction.GET_EXP, + { + value: MathFunction.GET_EXP, + name: 'Get exponent', + description: 'Returns the unbiased exponent used in the representation of an argument', + minArgs: 1, + maxArgs: 1 + } + ], + [ + MathFunction.HYPOT, + { + value: MathFunction.HYPOT, + name: 'Square root', + description: 'Returns the square root of the squares of the arguments', + minArgs: 2, + maxArgs: 2 + } + ], + [ + MathFunction.LOG, + { + value: MathFunction.LOG, + name: 'Logarithm', + description: 'Returns the natural logarithm of an argument', + minArgs: 1, + maxArgs: 1 + } + ], + [ + MathFunction.LOG10, + { + value: MathFunction.LOG10, + name: 'Base 10 logarithm', + description: 'Returns the base 10 logarithm of an argument', + minArgs: 1, + maxArgs: 1 + } + ], + [ + MathFunction.LOG1P, + { + value: MathFunction.LOG1P, + name: 'Logarithm of the sum', + description: 'Returns the natural logarithm of the sum of an argument', + minArgs: 1, + maxArgs: 1 + } + ], + [ + MathFunction.CEIL, + { + value: MathFunction.CEIL, + name: 'Ceiling', + description: 'Returns the smallest (closest to negative infinity) of an argument', + minArgs: 1, + maxArgs: 1 + } + ], + [ + MathFunction.FLOOR, + { + value: MathFunction.FLOOR, + name: 'Floor', + description: 'Returns the largest (closest to positive infinity) of an argument', + minArgs: 1, + maxArgs: 1 + } + ], + [ + MathFunction.FLOOR_DIV, + { + value: MathFunction.FLOOR_DIV, + name: 'Floor division', + description: 'Returns the largest (closest to positive infinity) of the arguments', + minArgs: 2, + maxArgs: 2 + } + ], + [ + MathFunction.FLOOR_MOD, + { + value: MathFunction.FLOOR_MOD, + name: 'Floor modulus', + description: 'Returns the floor modulus of the arguments', + minArgs: 2, + maxArgs: 2 + } + ], + [ + MathFunction.ABS, + { + value: MathFunction.ABS, + name: 'Absolute', + description: 'Returns the absolute value of an argument', + minArgs: 1, + maxArgs: 1 + } + ], + [ + MathFunction.MIN, + { + value: MathFunction.MIN, + name: 'Min', + description: 'Returns the smaller of the arguments', + minArgs: 2, + maxArgs: 2 + } + ], + [ + MathFunction.MAX, + { + value: MathFunction.MAX, + name: 'Max', + description: 'Returns the greater of the arguments', + minArgs: 2, + maxArgs: 2 + } + ], + [ + MathFunction.POW, + { + value: MathFunction.POW, + name: 'Raise to a power', + description: 'Returns the value of the first argument raised to the power of the second argument', + minArgs: 2, + maxArgs: 2 + } + ], + [ + MathFunction.SIGNUM, + { + value: MathFunction.SIGNUM, + name: 'Sign of a real number', + description: 'Returns the signum function of the argument', + minArgs: 1, + maxArgs: 1 + } + ], + [ + MathFunction.RAD, + { + value: MathFunction.RAD, + name: 'Radian', + description: 'Converts an angle measured in degrees to an approximately equivalent angle measured in radians', + minArgs: 1, + maxArgs: 1 + } + ], + [ + MathFunction.DEG, + { + value: MathFunction.DEG, + name: 'Degrees', + description: 'Converts an angle measured in radians to an approximately equivalent angle measured in degrees.', + minArgs: 1, + maxArgs: 1 + } + ], + ]); + +export enum ArgumentType { + MESSAGE_BODY = 'MESSAGE_BODY', + MESSAGE_METADATA = 'MESSAGE_METADATA', + ATTRIBUTE = 'ATTRIBUTE', + TIME_SERIES = 'TIME_SERIES', + CONSTANT = 'CONSTANT' +} + +export enum ArgumentTypeResult { + MESSAGE_BODY = 'MESSAGE_BODY', + MESSAGE_METADATA = 'MESSAGE_METADATA', + ATTRIBUTE = 'ATTRIBUTE', + TIME_SERIES = 'TIME_SERIES' +} + +export enum FetchTo { + DATA = 'DATA', + METADATA = 'METADATA' +} + +export const FetchFromToTranslation = new Map([ + [FetchTo.DATA, 'tb.rulenode.message-to-metadata'], + [FetchTo.METADATA, 'tb.rulenode.metadata-to-message'], +]); + +export const FetchFromTranslation = new Map([ + [FetchTo.DATA, 'tb.rulenode.from-message'], + [FetchTo.METADATA, 'tb.rulenode.from-metadata'], +]); + +export const FetchToTranslation = new Map([ + [FetchTo.DATA, 'tb.rulenode.message'], + [FetchTo.METADATA, 'tb.rulenode.metadata'], +]); + +export const FetchToRenameTranslation = new Map([ + [FetchTo.DATA, 'tb.rulenode.message'], + [FetchTo.METADATA, 'tb.rulenode.message-metadata'], +]); + +export interface ArgumentTypeData { + name: string; + description: string; +} + +export const ArgumentTypeMap = new Map([ + [ + ArgumentType.MESSAGE_BODY, + { + name: 'tb.rulenode.message-body-type', + description: 'Fetch argument value from incoming message' + } + ], + [ + ArgumentType.MESSAGE_METADATA, + { + name: 'tb.rulenode.message-metadata-type', + description: 'Fetch argument value from incoming message metadata' + } + ], + [ + ArgumentType.ATTRIBUTE, + { + name: 'tb.rulenode.attribute-type', + description: 'Fetch attribute value from database' + } + ], + [ + ArgumentType.TIME_SERIES, + { + name: 'tb.rulenode.time-series-type', + description: 'Fetch latest time-series value from database' + } + ], + [ + ArgumentType.CONSTANT, + { + name: 'tb.rulenode.constant-type', + description: 'Define constant value' + } + ] +]); + +export const ArgumentTypeResultMap = new Map([ + [ + ArgumentTypeResult.MESSAGE_BODY, + { + name: 'tb.rulenode.message-body-type', + description: 'Add result to the outgoing message' + } + ], + [ + ArgumentTypeResult.MESSAGE_METADATA, + { + name: 'tb.rulenode.message-metadata-type', + description: 'Add result to the outgoing message metadata' + } + ], + [ + ArgumentTypeResult.ATTRIBUTE, + { + name: 'tb.rulenode.attribute-type', + description: 'Store result as an entity attribute in the database' + } + ], + [ + ArgumentTypeResult.TIME_SERIES, + { + name: 'tb.rulenode.time-series-type', + description: 'Store result as an entity time-series in the database' + } + ] +]); + +export const ArgumentName = ['x', 'y', 'z', 'a', 'b', 'c', 'd', 'k', 'l', 'm', 'n', 'o', 'p', 'r', 's', 't']; + +export enum AttributeScope { + SHARED_SCOPE = 'SHARED_SCOPE', + SERVER_SCOPE = 'SERVER_SCOPE', + CLIENT_SCOPE = 'CLIENT_SCOPE' +} + +export enum AttributeScopeResult { + SHARED_SCOPE = 'SHARED_SCOPE', + SERVER_SCOPE = 'SERVER_SCOPE' +} + +export const AttributeScopeMap = new Map([ + [AttributeScope.SHARED_SCOPE, 'tb.rulenode.shared-scope'], + [AttributeScope.SERVER_SCOPE, 'tb.rulenode.server-scope'], + [AttributeScope.CLIENT_SCOPE, 'tb.rulenode.client-scope'] +]); + +export enum PresenceMonitoringStrategy { + ON_FIRST_MESSAGE = 'ON_FIRST_MESSAGE', + ON_EACH_MESSAGE = 'ON_EACH_MESSAGE' +} + +export interface PresenceMonitoringStrategyData { + value: boolean; + name: string; +} + +export const PresenceMonitoringStrategiesData = new Map([ + [ + PresenceMonitoringStrategy.ON_EACH_MESSAGE, + { + value: true, + name: 'tb.rulenode.presence-monitoring-strategy-on-each-message' + } + ], + [ + PresenceMonitoringStrategy.ON_FIRST_MESSAGE, + { + value: false, + name: 'tb.rulenode.presence-monitoring-strategy-on-first-message' + } + ] +]); + +export const IntLimit = 2147483648; diff --git a/ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.module.ts new file mode 100644 index 0000000000..79d46f02d5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.module.ts @@ -0,0 +1,75 @@ +/// +/// 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, Type } from '@angular/core'; +import { EmptyConfigComponent } from './empty-config.component'; +import { CommonModule } from '@angular/common'; +import { SharedModule } from '@shared/shared.module'; +import { + ruleNodeActionConfigComponentsMap, + RuleNodeConfigActionModule +} from '@home/components/rule-node/action/rule-node-config-action.module'; +import { + RuleNodeConfigFilterModule, + ruleNodeFilterConfigComponentsMap +} from '@home/components/rule-node/filter/rule-node-config-filter.module'; +import { + RuleNodeCoreEnrichmentModule, + ruleNodeEnrichmentConfigComponentsMap +} from '@home/components/rule-node/enrichment/rule-node-core-enrichment.module'; +import { + RuleNodeConfigExternalModule, + ruleNodeExternalConfigComponentsMap +} from '@home/components/rule-node/external/rule-node-config-external.module'; +import { + RuleNodeConfigTransformModule, + ruleNodeTransformConfigComponentsMap +} from '@home/components/rule-node/transform/rule-node-config-transform.module'; +import { + RuleNodeConfigFlowModule, + ruleNodeFlowConfigComponentsMap +} from '@home/components/rule-node/flow/rule-node-config-flow.module'; +import { IRuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; + +@NgModule({ + declarations: [ + EmptyConfigComponent + ], + imports: [ + CommonModule, + SharedModule + ], + exports: [ + RuleNodeConfigActionModule, + RuleNodeConfigFilterModule, + RuleNodeCoreEnrichmentModule, + RuleNodeConfigExternalModule, + RuleNodeConfigTransformModule, + RuleNodeConfigFlowModule, + EmptyConfigComponent + ] +}) +export class RuleNodeConfigModule {} + +export const ruleNodeConfigComponentsMap: Record> = { + ...ruleNodeActionConfigComponentsMap, + ...ruleNodeEnrichmentConfigComponentsMap, + ...ruleNodeExternalConfigComponentsMap, + ...ruleNodeFilterConfigComponentsMap, + ...ruleNodeFlowConfigComponentsMap, + ...ruleNodeTransformConfigComponentsMap, + 'tbNodeEmptyConfig': EmptyConfigComponent +}; diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/change-originator-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transform/change-originator-config.component.html new file mode 100644 index 0000000000..818ea5a12d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/transform/change-originator-config.component.html @@ -0,0 +1,66 @@ + +
    + + tb.rulenode.new-originator + + + + {{ originatorSourceTranslationMap.get(changeOriginatorConfigForm.get('originatorSource').value) | translate }} + + + + + {{ originatorSourceTranslationMap.get(source) | translate }} + +
    + + {{ originatorSourceDescTranslationMap.get(source) | translate }} + +
    +
    +
    +
    + + +
    + + + + tb.rulenode.entity-name-pattern + + + {{ 'tb.rulenode.entity-name-pattern-required' | translate }} + + +
    +
    + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/change-originator-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/transform/change-originator-config.component.ts new file mode 100644 index 0000000000..954dd412e5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/transform/change-originator-config.component.ts @@ -0,0 +1,83 @@ +/// +/// 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 { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { + OriginatorSource, + originatorSourceDescTranslations, + originatorSourceTranslations +} from '@home/components/rule-node/rule-node-config.models'; +import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models'; +import { EntityType } from '@app/shared/models/entity-type.models'; + +@Component({ + selector: 'tb-transformation-node-change-originator-config', + templateUrl: './change-originator-config.component.html' +}) +export class ChangeOriginatorConfigComponent extends RuleNodeConfigurationComponent { + + originatorSource = OriginatorSource; + originatorSources = Object.keys(OriginatorSource) as OriginatorSource[]; + originatorSourceTranslationMap = originatorSourceTranslations; + originatorSourceDescTranslationMap = originatorSourceDescTranslations; + + changeOriginatorConfigForm: FormGroup; + + allowedEntityTypes = [EntityType.DEVICE, EntityType.ASSET, EntityType.ENTITY_VIEW, EntityType.USER, EntityType.EDGE]; + + constructor(private fb: FormBuilder) { + super(); + } + + protected configForm(): FormGroup { + return this.changeOriginatorConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.changeOriginatorConfigForm = this.fb.group({ + originatorSource: [configuration ? configuration.originatorSource : null, [Validators.required]], + entityType: [configuration ? configuration.entityType : null, []], + entityNamePattern: [configuration ? configuration.entityNamePattern : null, []], + relationsQuery: [configuration ? configuration.relationsQuery : null, []] + }); + } + + protected validatorTriggers(): string[] { + return ['originatorSource']; + } + + protected updateValidators(emitEvent: boolean) { + const originatorSource: OriginatorSource = this.changeOriginatorConfigForm.get('originatorSource').value; + if (originatorSource === OriginatorSource.RELATED) { + this.changeOriginatorConfigForm.get('relationsQuery').setValidators([Validators.required]); + } else { + this.changeOriginatorConfigForm.get('relationsQuery').setValidators([]); + } + if (originatorSource === OriginatorSource.ENTITY) { + this.changeOriginatorConfigForm.get('entityType').setValidators([Validators.required]); + this.changeOriginatorConfigForm.get('entityNamePattern').setValidators([Validators.required, Validators.pattern(/.*\S.*/)]); + } else { + this.changeOriginatorConfigForm.get('entityType').patchValue(null, {emitEvent}); + this.changeOriginatorConfigForm.get('entityNamePattern').patchValue(null, {emitEvent}); + this.changeOriginatorConfigForm.get('entityType').setValidators([]); + this.changeOriginatorConfigForm.get('entityNamePattern').setValidators([]); + } + this.changeOriginatorConfigForm.get('relationsQuery').updateValueAndValidity({emitEvent}); + this.changeOriginatorConfigForm.get('entityType').updateValueAndValidity({emitEvent}); + this.changeOriginatorConfigForm.get('entityNamePattern').updateValueAndValidity({emitEvent}); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/copy-keys-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transform/copy-keys-config.component.html new file mode 100644 index 0000000000..85cf878a4b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/transform/copy-keys-config.component.html @@ -0,0 +1,36 @@ + +
    + + + + + help + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/copy-keys-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/transform/copy-keys-config.component.ts new file mode 100644 index 0000000000..1fcf4a7053 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/transform/copy-keys-config.component.ts @@ -0,0 +1,73 @@ +/// +/// 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 { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { isDefinedAndNotNull } from '@core/public-api'; +import { TranslateService } from '@ngx-translate/core'; +import { FetchFromToTranslation, FetchTo } from '../rule-node-config.models'; +import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models'; + +@Component({ + selector: 'tb-transformation-node-copy-keys-config', + templateUrl: './copy-keys-config.component.html', + styleUrls: [] +}) + +export class CopyKeysConfigComponent extends RuleNodeConfigurationComponent{ + copyKeysConfigForm: FormGroup; + copyFrom = []; + translation = FetchFromToTranslation; + + constructor(private fb: FormBuilder, + private translate: TranslateService) { + super(); + for (const key of this.translation.keys()) { + this.copyFrom.push({ + value: key, + name: this.translate.instant(this.translation.get(key)) + }); + } + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.copyKeysConfigForm = this.fb.group({ + copyFrom: [configuration.copyFrom , [Validators.required]], + keys: [configuration ? configuration.keys : null, [Validators.required]] + }); + } + + protected configForm(): FormGroup { + return this.copyKeysConfigForm; + } + + protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + let copyFrom: FetchTo; + + if (isDefinedAndNotNull(configuration?.fromMetadata)) { + copyFrom = configuration.copyFrom ? FetchTo.METADATA : FetchTo.DATA; + } else if (isDefinedAndNotNull(configuration?.copyFrom)) { + copyFrom = configuration.copyFrom; + } else { + copyFrom = FetchTo.DATA; + } + + return { + keys: isDefinedAndNotNull(configuration?.keys) ? configuration.keys : null, + copyFrom + }; + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/deduplication-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transform/deduplication-config.component.html new file mode 100644 index 0000000000..e6309df7eb --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/transform/deduplication-config.component.html @@ -0,0 +1,100 @@ + +
    + + {{'tb.rulenode.interval' | translate}} + + + {{'tb.rulenode.interval-required' | translate}} + + + {{'tb.rulenode.interval-min-error' | translate}} + + help + +
    +
    +
    tb.rulenode.strategy
    + + + {{ deduplicationStrategiesTranslations.get(strategy) | translate }} + + + + + + + + +
    + + +
    +
    +
    + + + tb.rulenode.advanced-settings + +
    + + {{'tb.rulenode.max-pending-msgs' | translate}} + + + {{'tb.rulenode.max-pending-msgs-required' | translate}} + + + {{'tb.rulenode.max-pending-msgs-max-error' | translate}} + + + {{'tb.rulenode.max-pending-msgs-min-error' | translate}} + + help + + + {{'tb.rulenode.max-retries' | translate}} + + + {{'tb.rulenode.max-retries-required' | translate}} + + + {{'tb.rulenode.max-retries-max-error' | translate}} + + + {{'tb.rulenode.max-retries-min-error' | translate}} + + help + +
    +
    +
    +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/deduplication-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/transform/deduplication-config.component.ts new file mode 100644 index 0000000000..ec9b3a9cf2 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/transform/deduplication-config.component.ts @@ -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. +/// + +import { Component } from '@angular/core'; +import { isDefinedAndNotNull } from '@core/public-api'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { deduplicationStrategiesTranslations, FetchMode } from '@home/components/rule-node/rule-node-config.models'; +import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; + +@Component({ + selector: 'tb-action-node-msg-deduplication-config', + templateUrl: './deduplication-config.component.html', + styleUrls: [] +}) + +export class DeduplicationConfigComponent extends RuleNodeConfigurationComponent { + + deduplicationConfigForm: FormGroup; + deduplicationStrategie = FetchMode; + deduplicationStrategies = Object.keys(this.deduplicationStrategie); + deduplicationStrategiesTranslations = deduplicationStrategiesTranslations; + + constructor(private fb: FormBuilder) { + super(); + } + + protected configForm(): FormGroup { + return this.deduplicationConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.deduplicationConfigForm = this.fb.group({ + interval: [isDefinedAndNotNull(configuration?.interval) ? configuration.interval : null, [Validators.required, + Validators.min(1)]], + strategy: [isDefinedAndNotNull(configuration?.strategy) ? configuration.strategy : null, [Validators.required]], + outMsgType: [isDefinedAndNotNull(configuration?.outMsgType) ? configuration.outMsgType : null, [Validators.required]], + maxPendingMsgs: [isDefinedAndNotNull(configuration?.maxPendingMsgs) ? configuration.maxPendingMsgs : null, [Validators.required, + Validators.min(1), Validators.max(1000)]], + maxRetries: [isDefinedAndNotNull(configuration?.maxRetries) ? configuration.maxRetries : null, + [Validators.required, Validators.min(0), Validators.max(100)]] + }); + } + + protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + if (!configuration) { + configuration = {}; + } + if (!configuration.outMsgType) { + configuration.outMsgType = 'POST_TELEMETRY_REQUEST'; + } + return super.prepareInputConfig(configuration); + } + + protected updateValidators(emitEvent: boolean) { + if (this.deduplicationConfigForm.get('strategy').value === this.deduplicationStrategie.ALL) { + this.deduplicationConfigForm.get('outMsgType').enable({emitEvent: false}); + } else { + this.deduplicationConfigForm.get('outMsgType').disable({emitEvent: false}); + } + this.deduplicationConfigForm.get('outMsgType').updateValueAndValidity({emitEvent}); + } + + protected validatorTriggers(): string[] { + return ['strategy']; + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/delete-keys-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transform/delete-keys-config.component.html new file mode 100644 index 0000000000..23737bab9f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/transform/delete-keys-config.component.html @@ -0,0 +1,35 @@ + +
    + + + + + help + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/delete-keys-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/transform/delete-keys-config.component.ts new file mode 100644 index 0000000000..a5e95ef6ce --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/transform/delete-keys-config.component.ts @@ -0,0 +1,74 @@ +/// +/// 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 { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { isDefinedAndNotNull } from '@core/public-api'; +import { TranslateService } from '@ngx-translate/core'; +import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models'; +import { FetchTo, FetchToTranslation } from '@home/components/rule-node/rule-node-config.models'; + +@Component({ + selector: 'tb-transformation-node-delete-keys-config', + templateUrl: './delete-keys-config.component.html', + styleUrls: [] +}) + +export class DeleteKeysConfigComponent extends RuleNodeConfigurationComponent { + + deleteKeysConfigForm: FormGroup; + deleteFrom = []; + translation = FetchToTranslation; + + constructor(private fb: FormBuilder, + private translate: TranslateService) { + super(); + for (const key of this.translation.keys()) { + this.deleteFrom.push({ + value: key, + name: this.translate.instant(this.translation.get(key)) + }); + } + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.deleteKeysConfigForm = this.fb.group({ + deleteFrom: [configuration.deleteFrom, [Validators.required]], + keys: [configuration ? configuration.keys : null, [Validators.required]] + }); + } + + protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + let deleteFrom: FetchTo; + + if (isDefinedAndNotNull(configuration?.fromMetadata)) { + deleteFrom = configuration.fromMetadata ? FetchTo.METADATA : FetchTo.DATA; + } else if (isDefinedAndNotNull(configuration?.deleteFrom)) { + deleteFrom = configuration?.deleteFrom; + } else { + deleteFrom = FetchTo.DATA; + } + + return { + keys: isDefinedAndNotNull(configuration?.keys) ? configuration.keys : null, + deleteFrom + }; + } + + protected configForm(): FormGroup { + return this.deleteKeysConfigForm; + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/node-json-path-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transform/node-json-path-config.component.html new file mode 100644 index 0000000000..c1d3c8e2e4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/transform/node-json-path-config.component.html @@ -0,0 +1,25 @@ + +
    + + {{ 'tb.rulenode.json-path-expression' | translate }} + + {{ 'tb.rulenode.json-path-expression-hint' | translate }} + {{ 'tb.rulenode.json-path-expression-required' | translate }} + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/node-json-path-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/transform/node-json-path-config.component.ts new file mode 100644 index 0000000000..47185b1d51 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/transform/node-json-path-config.component.ts @@ -0,0 +1,44 @@ +/// +/// 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 { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; + +@Component({ + selector: 'tb-transformation-node-json-path-config', + templateUrl: './node-json-path-config.component.html', + styleUrls: [] +}) + +export class NodeJsonPathConfigComponent extends RuleNodeConfigurationComponent { + + jsonPathConfigForm: FormGroup; + + constructor(private fb: FormBuilder) { + super(); + } + + protected configForm(): FormGroup { + return this.jsonPathConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.jsonPathConfigForm = this.fb.group({ + jsonPath: [configuration ? configuration.jsonPath : null, [Validators.required]], + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/rename-keys-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transform/rename-keys-config.component.html new file mode 100644 index 0000000000..3099a31e4e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/transform/rename-keys-config.component.html @@ -0,0 +1,40 @@ + +
    +
    tb.rulenode.rename-keys-in
    +
    +
    + + + {{ data.name }} + + +
    +
    + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/rename-keys-config.component.scss b/ui-ngx/src/app/modules/home/components/rule-node/transform/rename-keys-config.component.scss new file mode 100644 index 0000000000..1c767d5d68 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/transform/rename-keys-config.component.scss @@ -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. + */ +:host { + .fetch-to-data-toggle { + max-width: 420px; + width: 100%; + } + + .fx-centered { + display: flex; + width: 100%; + justify-content: space-around; + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/rename-keys-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/transform/rename-keys-config.component.ts new file mode 100644 index 0000000000..ebc7393b05 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/transform/rename-keys-config.component.ts @@ -0,0 +1,73 @@ +/// +/// 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 { TranslateService } from '@ngx-translate/core'; +import { FetchTo, FetchToRenameTranslation } from '../rule-node-config.models'; +import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; + + +@Component({ + selector: 'tb-transformation-node-rename-keys-config', + templateUrl: './rename-keys-config.component.html', + styleUrls: ['./rename-keys-config.component.scss'] +}) +export class RenameKeysConfigComponent extends RuleNodeConfigurationComponent { + renameKeysConfigForm: FormGroup; + renameIn = []; + translation = FetchToRenameTranslation; + + constructor(private fb: FormBuilder, + private translate: TranslateService) { + super(); + for (const key of this.translation.keys()) { + this.renameIn.push({ + value: key, + name: this.translate.instant(this.translation.get(key)) + }); + } + } + + protected configForm(): FormGroup { + return this.renameKeysConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.renameKeysConfigForm = this.fb.group({ + renameIn: [configuration ? configuration.renameIn : null, [Validators.required]], + renameKeysMapping: [configuration ? configuration.renameKeysMapping : null, [Validators.required]] + }); + } + + protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + let renameIn: FetchTo; + + if (isDefinedAndNotNull(configuration?.fromMetadata)) { + renameIn = configuration.fromMetadata ? FetchTo.METADATA : FetchTo.DATA; + } else if (isDefinedAndNotNull(configuration?.renameIn)) { + renameIn = configuration?.renameIn; + } else { + renameIn = FetchTo.DATA; + } + + return { + renameKeysMapping: isDefinedAndNotNull(configuration?.renameKeysMapping) ? configuration.renameKeysMapping : null, + renameIn + }; + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/rule-node-config-transform.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/transform/rule-node-config-transform.module.ts new file mode 100644 index 0000000000..c64381e54e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/transform/rule-node-config-transform.module.ts @@ -0,0 +1,70 @@ +/// +/// 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, Type } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { IRuleNodeConfigurationComponent, SharedModule } from '@shared/public-api'; +import { ChangeOriginatorConfigComponent } from './change-originator-config.component'; +import { RuleNodeConfigCommonModule } from '../common/rule-node-config-common.module'; +import { TransformScriptConfigComponent } from './script-config.component'; +import { ToEmailConfigComponent } from './to-email-config.component'; +import { CopyKeysConfigComponent } from './copy-keys-config.component'; +import { RenameKeysConfigComponent } from './rename-keys-config.component'; +import { NodeJsonPathConfigComponent } from './node-json-path-config.component'; +import { DeleteKeysConfigComponent } from './delete-keys-config.component'; +import { DeduplicationConfigComponent } from './deduplication-config.component'; +import { ScriptConfigComponent } from '@home/components/rule-node/filter/script-config.component'; + +@NgModule({ + declarations: [ + ChangeOriginatorConfigComponent, + TransformScriptConfigComponent, + ToEmailConfigComponent, + CopyKeysConfigComponent, + RenameKeysConfigComponent, + NodeJsonPathConfigComponent, + DeleteKeysConfigComponent, + DeduplicationConfigComponent + ], + imports: [ + CommonModule, + SharedModule, + RuleNodeConfigCommonModule + ], + exports: [ + ChangeOriginatorConfigComponent, + TransformScriptConfigComponent, + ToEmailConfigComponent, + CopyKeysConfigComponent, + RenameKeysConfigComponent, + NodeJsonPathConfigComponent, + DeleteKeysConfigComponent, + DeduplicationConfigComponent + ] +}) +export class RuleNodeConfigTransformModule { +} + +export const ruleNodeTransformConfigComponentsMap: Record> = { + 'tbTransformationNodeChangeOriginatorConfig': ChangeOriginatorConfigComponent, + 'tbTransformationNodeCopyKeysConfig': CopyKeysConfigComponent, + 'tbActionNodeMsgDeduplicationConfig': DeduplicationConfigComponent, + 'tbTransformationNodeDeleteKeysConfig': DeleteKeysConfigComponent, + 'tbTransformationNodeJsonPathConfig': NodeJsonPathConfigComponent, + 'tbTransformationNodeRenameKeysConfig': RenameKeysConfigComponent, + 'tbTransformationNodeScriptConfig': ScriptConfigComponent, + 'tbTransformationNodeToEmailConfig': ToEmailConfigComponent +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/script-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transform/script-config.component.html new file mode 100644 index 0000000000..efabc8d940 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/transform/script-config.component.html @@ -0,0 +1,59 @@ + +
    + + + + + + + +
    + +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/script-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/transform/script-config.component.ts new file mode 100644 index 0000000000..7e462ee039 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/transform/script-config.component.ts @@ -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. +/// + +import { Component, EventEmitter, ViewChild } from '@angular/core'; +import { getCurrentAuthState, NodeScriptTestService } from '@core/public-api'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { TranslateService } from '@ngx-translate/core'; +import type { JsFuncComponent } from '@shared/components/js-func.component'; +import { + RuleNodeConfiguration, + RuleNodeConfigurationComponent, + ScriptLanguage +} from '@app/shared/models/rule-node.models'; +import { DebugRuleNodeEventBody } from '@shared/models/event.models'; + +@Component({ + selector: 'tb-transformation-node-script-config', + templateUrl: './script-config.component.html', + styleUrls: [] +}) +export class TransformScriptConfigComponent extends RuleNodeConfigurationComponent { + + @ViewChild('jsFuncComponent', {static: false}) jsFuncComponent: JsFuncComponent; + @ViewChild('tbelFuncComponent', {static: false}) tbelFuncComponent: JsFuncComponent; + + scriptConfigForm: FormGroup; + + tbelEnabled = getCurrentAuthState(this.store).tbelEnabled; + + scriptLanguage = ScriptLanguage; + + changeScript: EventEmitter = new EventEmitter(); + + readonly hasScript = true; + + readonly testScriptLabel = 'tb.rulenode.test-transformer-function'; + + constructor(private fb: FormBuilder, + private nodeScriptTestService: NodeScriptTestService, + private translate: TranslateService) { + super(); + } + + protected configForm(): FormGroup { + return this.scriptConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.scriptConfigForm = this.fb.group({ + scriptLang: [configuration ? configuration.scriptLang : ScriptLanguage.JS, [Validators.required]], + jsScript: [configuration ? configuration.jsScript : null, [Validators.required]], + tbelScript: [configuration ? configuration.tbelScript : null, []] + }); + } + + protected validatorTriggers(): string[] { + return ['scriptLang']; + } + + protected updateValidators(emitEvent: boolean) { + let scriptLang: ScriptLanguage = this.scriptConfigForm.get('scriptLang').value; + if (scriptLang === ScriptLanguage.TBEL && !this.tbelEnabled) { + scriptLang = ScriptLanguage.JS; + this.scriptConfigForm.get('scriptLang').patchValue(scriptLang, {emitEvent: false}); + setTimeout(() => { + this.scriptConfigForm.updateValueAndValidity({emitEvent: true}); + }); + } + this.scriptConfigForm.get('jsScript').setValidators(scriptLang === ScriptLanguage.JS ? [Validators.required] : []); + this.scriptConfigForm.get('jsScript').updateValueAndValidity({emitEvent}); + this.scriptConfigForm.get('tbelScript').setValidators(scriptLang === ScriptLanguage.TBEL ? [Validators.required] : []); + this.scriptConfigForm.get('tbelScript').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.scriptConfigForm.get('scriptLang').value; + const scriptField = scriptLang === ScriptLanguage.JS ? 'jsScript' : 'tbelScript'; + const helpId = scriptLang === ScriptLanguage.JS + ? 'rulenode/transformation_node_script_fn' + : 'rulenode/tbel/transformation_node_script_fn'; + const script: string = this.scriptConfigForm.get(scriptField).value; + this.nodeScriptTestService.testNodeScript( + script, + 'update', + this.translate.instant('tb.rulenode.transformer'), + 'Transform', + ['msg', 'metadata', 'msgType'], + this.ruleNodeId, + helpId, + scriptLang, + debugEventBody + ).subscribe((theScript) => { + if (theScript) { + this.scriptConfigForm.get(scriptField).setValue(theScript); + this.changeScript.emit(); + } + }); + } + + protected onValidate() { + const scriptLang: ScriptLanguage = this.scriptConfigForm.get('scriptLang').value; + if (scriptLang === ScriptLanguage.JS) { + this.jsFuncComponent.validateOnSubmit(); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/to-email-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transform/to-email-config.component.html new file mode 100644 index 0000000000..f28a6d4737 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/transform/to-email-config.component.html @@ -0,0 +1,139 @@ + +
    +
    +
    tb.rulenode.email-sender
    +
    + + tb.rulenode.from-template + + + {{ 'tb.rulenode.email-from-template-hint' | translate }} + + +
    +
    +
    +
    + + {{ 'tb.rulenode.from-template-required' | translate }} + +
    +
    +
    +
    +
    +
    tb.rulenode.recipients
    + + +
    +
    + + tb.rulenode.to-template + + + {{ 'tb.rulenode.to-template-required' | translate }} + + + + tb.rulenode.cc-template + + + + tb.rulenode.bcc-template + + +
    +
    +
    +
    tb.rulenode.message-subject-and-content
    + + +
    + + tb.rulenode.subject-template + + + {{ 'tb.rulenode.subject-template-required' | translate }} + + + + tb.rulenode.mail-body-type + + + + {{ getBodyTypeName() | translate }} + + + + + {{ type.name | translate }} + +
    + + {{ type.description | translate }} + +
    +
    +
    + + tb.rulenode.body-type-template + + tb.mail-body-type.after-template-evaluation-hint + + + tb.rulenode.body-template + + + {{ 'tb.rulenode.body-template-required' | translate }} + + +
    +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/to-email-config.component.scss b/ui-ngx/src/app/modules/home/components/rule-node/transform/to-email-config.component.scss new file mode 100644 index 0000000000..2a0c2e6ea3 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/transform/to-email-config.component.scss @@ -0,0 +1,29 @@ +/** + * 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 { + .input-bottom-double-hint { + display: inline-flex; + + & .see-example { + flex-shrink: 0; + padding-right: 16px; + } + } + + textarea.tb-enable-vertical-resize { + resize: vertical; + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/to-email-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/transform/to-email-config.component.ts new file mode 100644 index 0000000000..3c922a7067 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/transform/to-email-config.component.ts @@ -0,0 +1,98 @@ +/// +/// 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 { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models'; + +@Component({ + selector: 'tb-transformation-node-to-email-config', + templateUrl: './to-email-config.component.html', + styleUrls: ['./to-email-config.component.scss'] +}) +export class ToEmailConfigComponent extends RuleNodeConfigurationComponent { + + toEmailConfigForm: FormGroup; + mailBodyTypes = [ + { + name: 'tb.mail-body-type.plain-text', + description: 'tb.mail-body-type.plain-text-description', + value: 'false', + }, + { + name: 'tb.mail-body-type.html', + description: 'tb.mail-body-type.html-text-description', + value: 'true', + }, + { + name: 'tb.mail-body-type.use-body-type-template', + description: 'tb.mail-body-type.dynamic-text-description', + value: 'dynamic', + } + ]; + + constructor(private fb: FormBuilder) { + super(); + } + + protected configForm(): FormGroup { + return this.toEmailConfigForm; + } + + protected onConfigurationSet(configuration: RuleNodeConfiguration) { + this.toEmailConfigForm = this.fb.group({ + fromTemplate: [configuration ? configuration.fromTemplate : null, [Validators.required]], + toTemplate: [configuration ? configuration.toTemplate : null, [Validators.required]], + ccTemplate: [configuration ? configuration.ccTemplate : null, []], + bccTemplate: [configuration ? configuration.bccTemplate : null, []], + subjectTemplate: [configuration ? configuration.subjectTemplate : null, [Validators.required]], + mailBodyType: [configuration ? configuration.mailBodyType : null], + isHtmlTemplate: [configuration ? configuration.isHtmlTemplate : null, [Validators.required]], + bodyTemplate: [configuration ? configuration.bodyTemplate : null, [Validators.required]], + }); + } + + protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { + return { + fromTemplate: isDefinedAndNotNull(configuration?.fromTemplate) ? configuration.fromTemplate : null, + toTemplate: isDefinedAndNotNull(configuration?.toTemplate) ? configuration.toTemplate : null, + ccTemplate: isDefinedAndNotNull(configuration?.ccTemplate) ? configuration.ccTemplate : null, + bccTemplate: isDefinedAndNotNull(configuration?.bccTemplate) ? configuration.bccTemplate : null, + subjectTemplate: isDefinedAndNotNull(configuration?.subjectTemplate) ? configuration.subjectTemplate : null, + mailBodyType: isDefinedAndNotNull(configuration?.mailBodyType) ? configuration.mailBodyType : null, + isHtmlTemplate: isDefinedAndNotNull(configuration?.isHtmlTemplate) ? configuration.isHtmlTemplate : null, + bodyTemplate: isDefinedAndNotNull(configuration?.bodyTemplate) ? configuration.bodyTemplate : null, + }; + } + + protected updateValidators(emitEvent: boolean) { + if (this.toEmailConfigForm.get('mailBodyType').value === 'dynamic') { + this.toEmailConfigForm.get('isHtmlTemplate').enable({emitEvent: false}); + } else { + this.toEmailConfigForm.get('isHtmlTemplate').disable({emitEvent: false}); + } + this.toEmailConfigForm.get('isHtmlTemplate').updateValueAndValidity({emitEvent}); + } + + protected validatorTriggers(): string[] { + return ['mailBodyType']; + } + + getBodyTypeName(): string { + return this.mailBodyTypes.find(type => type.value === this.toEmailConfigForm.get('mailBodyType').value).name; + } +} diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-config.component.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-config.component.ts index fc73cc9d56..029c4b5547 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-config.component.ts +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-config.component.ts @@ -21,7 +21,7 @@ import { forwardRef, Input, OnDestroy, - Output, + Output, Type, ViewChild, ViewContainerRef } from '@angular/core'; @@ -43,6 +43,7 @@ import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { JsonObjectEditComponent } from '@shared/components/json-object-edit.component'; import { deepClone } from '@core/utils'; import { RuleChainType } from '@shared/models/rule-chain.models'; +import { ruleNodeConfigComponentsMap } from '@home/components/rule-node/rule-node-config.module'; @Component({ selector: 'tb-rule-node-config', @@ -207,8 +208,17 @@ export class RuleNodeConfigComponent implements ControlValueAccessor, OnDestroy this.changeSubscription.unsubscribe(); this.changeSubscription = null; } + if (this.changeScriptSubscription) { + this.changeScriptSubscription.unsubscribe(); + this.changeScriptSubscription = null; + } this.definedConfigContainer.clear(); - const component = this.ruleChainService.getRuleNodeConfigComponent(this.nodeDefinition.configDirective); + let component: Type; + if (!this.nodeDefinition.uiResources?.length) { + component = ruleNodeConfigComponentsMap[this.nodeDefinition.configDirective]; + } else { + component = this.ruleChainService.getRuleNodeConfigComponent(this.nodeDefinition.configDirective); + } this.definedConfigComponentRef = this.definedConfigContainer.createComponent(component); this.definedConfigComponent = this.definedConfigComponentRef.instance; this.definedConfigComponent.ruleNodeId = this.ruleNodeId; diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts index 4cc83b0074..00fa560382 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts @@ -34,6 +34,7 @@ import { LinkLabelsComponent } from '@home/pages/rulechain/link-labels.component import { RuleNodeConfigComponent } from './rule-node-config.component'; import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe'; import { EntityDebugSettingsButtonComponent } from '@home/components/entity/debug/entity-debug-settings-button.component'; +import { RuleNodeConfigModule } from '@home/components/rule-node/rule-node-config.module'; @NgModule({ declarations: [ @@ -63,7 +64,8 @@ import { EntityDebugSettingsButtonComponent } from '@home/components/entity/debu HomeComponentsModule, RuleChainRoutingModule, DurationLeftPipe, - EntityDebugSettingsButtonComponent + EntityDebugSettingsButtonComponent, + RuleNodeConfigModule, ] }) export class RuleChainModule { } diff --git a/ui-ngx/src/app/shared/models/rule-node.models.ts b/ui-ngx/src/app/shared/models/rule-node.models.ts index a79fdbc376..86e304b3df 100644 --- a/ui-ngx/src/app/shared/models/rule-node.models.ts +++ b/ui-ngx/src/app/shared/models/rule-node.models.ts @@ -21,9 +21,7 @@ import { ComponentDescriptor } from '@shared/models/component-descriptor.models' import { FcEdge, FcNode } from 'ngx-flowchart'; import { Observable } from 'rxjs'; import { PageComponent } from '@shared/components/page.component'; -import { AfterViewInit, EventEmitter, Inject, OnInit, Directive, DestroyRef, inject } from '@angular/core'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; +import { AfterViewInit, DestroyRef, Directive, EventEmitter, inject, OnInit } from '@angular/core'; import { AbstractControl, UntypedFormGroup } from '@angular/forms'; import { RuleChainType } from '@shared/models/rule-chain.models'; import { DebugRuleNodeEventBody } from '@shared/models/event.models'; @@ -134,8 +132,8 @@ export abstract class RuleNodeConfigurationComponent extends PageComponent imple configurationChangedEmiter = new EventEmitter(); configurationChanged = this.configurationChangedEmiter.asObservable(); - protected constructor(@Inject(Store) protected store: Store) { - super(store); + protected constructor(...args: unknown[]) { + super(); } ngOnInit() {} diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 427fbdbe14..bbcc04d5f9 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -4481,6 +4481,734 @@ "too-many-requests": "Too many requests", "too-many-updates": "Too many updates" }, + "tb": { + "rulenode": { + "id": "Id", + "additional-info": "Additional Info", + "advanced-settings": "Advanced settings", + "create-entity-if-not-exists": "Create new entity if it doesn't exist", + "create-entity-if-not-exists-hint": "If enabled, a new entity with specified parameters will be created unless it already exists. Existing entities will be used as is for relation.", + "select-device-connectivity-event": "Select device connectivity event", + "entity-name-pattern": "Name pattern", + "device-name-pattern": "Device name", + "asset-name-pattern": "Asset name", + "entity-view-name-pattern": "Entity view name", + "customer-title-pattern": "Customer title", + "dashboard-name-pattern": "Dashboard title", + "user-name-pattern": "User email", + "edge-name-pattern": "Edge name", + "entity-name-pattern-required": "Name pattern is required", + "entity-name-pattern-hint": "Name pattern field support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", + "copy-message-type": "Copy message type", + "entity-type-pattern": "Type pattern", + "entity-type-pattern-required": "Type pattern is required", + "message-type-value": "Message type value", + "message-type-value-required": "Message type value is required", + "message-type-value-max-length": "Message type value should be less than 256", + "output-message-type": "Output message type", + "entity-cache-expiration": "Entities cache expiration time (sec)", + "entity-cache-expiration-hint": "Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.", + "entity-cache-expiration-required": "Entities cache expiration time is required.", + "entity-cache-expiration-range": "Entities cache expiration time should be greater than or equal to 0.", + "customer-name-pattern": "Customer title", + "customer-name-pattern-required": "Customer title is required", + "customer-name-pattern-hint": "Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", + "create-customer-if-not-exists": "Create new customer if it doesn't exist", + "unassign-from-customer": "Unassign from specific customer if originator is dashboard", + "unassign-from-customer-tooltip": "Only dashboards can be assigned to multiple customers at once. \nIf the message originator is a dashboard, you need to explicitly specify the customer's title to unassign from.", + "customer-cache-expiration": "Customers cache expiration time (sec)", + "customer-cache-expiration-hint": "Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.", + "customer-cache-expiration-required": "Customers cache expiration time is required.", + "customer-cache-expiration-range": "Customers cache expiration time should be greater than or equal to 0.", + "interval-start": "Interval start", + "interval-end": "Interval end", + "time-unit": "Time unit", + "fetch-mode": "Fetch mode", + "order-by-timestamp": "Order by timestamp", + "limit": "Limit", + "limit-hint": "Min limit value is 2, max - 1000. If you want to fetch a single entry, select fetch mode 'First' or 'Last'.", + "limit-required": "Limit is required.", + "limit-range": "Limit should be in a range from 2 to 1000.", + "time-unit-milliseconds": "Milliseconds", + "time-unit-seconds": "Seconds", + "time-unit-minutes": "Minutes", + "time-unit-hours": "Hours", + "time-unit-days": "Days", + "time-value-range": "Allowing range from 1 to 2147483647.", + "start-interval-value-required": "Interval start is required.", + "end-interval-value-required": "Interval end is required.", + "filter": "Filter", + "switch": "Switch", + "math-templatization-tooltip": "This field support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", + "add-message-type": "Add message type", + "select-message-types-required": "At least one message type should be selected.", + "select-message-types": "Select message types", + "no-message-types-found": "No message types found", + "no-message-type-matching": "'{{messageType}}' not found.", + "create-new-message-type": "Create a new one.", + "message-types-required": "Message types are required.", + "client-attributes": "Client attributes", + "shared-attributes": "Shared attributes", + "server-attributes": "Server attributes", + "attributes-keys": "Attributes keys", + "attributes-keys-required": "Attributes keys are required", + "attributes-scope": "Attributes scope", + "attributes-scope-value": "Attributes scope value", + "attributes-scope-value-copy": "Copy attributes scope value", + "attributes-scope-hint": "Use the 'scope' metadata key to dynamically set the attribute scope per message. If provided, this overrides the scope set in the configuration.", + "notify-device": "Force notification to the device", + "send-attributes-updated-notification": "Send attributes updated notification", + "send-attributes-updated-notification-hint": "Send notification about updated attributes as a separate message to the rule engine queue.", + "send-attributes-deleted-notification": "Send attributes deleted notification", + "send-attributes-deleted-notification-hint": "Send notification about deleted attributes as a separate message to the rule engine queue.", + "update-attributes-only-on-value-change": "Save attributes only if the value changes", + "update-attributes-only-on-value-change-hint": "Updates the attributes on every incoming message disregarding if their value has changed. Increases API usage and reduces performance.", + "update-attributes-only-on-value-change-hint-enabled": "Updates the attributes only if their value has changed. If the value is not changed, no update to the attribute timestamp nor attribute change notification will be sent.", + "fetch-credentials-to-metadata": "Fetch credentials to metadata", + "notify-device-on-update-hint": "If enabled, force notification to the device about shared attributes update. If disabled, the notification behavior is controlled by the 'notifyDevice' parameter from the incoming message metadata. To turn off the notification, the message metadata must contain the 'notifyDevice' parameter set to 'false'. Any other case will trigger the notification to the device.", + "notify-device-on-delete-hint": "If enabled, force notification to the device about shared attributes removal. If disabled, the notification behavior is controlled by the 'notifyDevice' parameter from the incoming message metadata. To turn on the notification, the message metadata must contain the 'notifyDevice' parameter set to 'true'. In any other case, the notification will not be triggered to the device.", + "latest-timeseries": "Latest time series data keys", + "timeseries-keys": "Time series keys", + "timeseries-keys-required": "At least one time series key should be selected.", + "add-timeseries-key": "Add time series key", + "add-message-field": "Add message field", + "relation-search-parameters": "Relation search parameters", + "relation-parameters": "Relation parameters", + "add-metadata-field": "Add metadata field", + "data-keys": "Message field names", + "copy-from": "Copy from", + "data-to-metadata": "Data to metadata", + "metadata-to-data": "Metadata to data", + "use-regular-expression-hint": "Use regular expression to copy keys by pattern.\n\nTips & tricks:\nPress 'Enter' to complete field name input.\nPress 'Backspace' to delete field name. Multiple field names supported.", + "interval": "Interval", + "interval-required": "Interval is required", + "interval-hint": "Deduplication interval in seconds.", + "interval-min-error": "Min allowed value is 1", + "max-pending-msgs": "Max pending messages", + "max-pending-msgs-hint": "Maximum number of messages that are stored in memory for each unique deduplication id.", + "max-pending-msgs-required": "Max pending messages is required", + "max-pending-msgs-max-error": "Max allowed value is 1000", + "max-pending-msgs-min-error": "Min allowed value is 1", + "max-retries": "Max retries", + "max-retries-required": "Max retries is required", + "max-retries-hint": "Maximum number of retries to push the deduplicated messages into the queue. 10 seconds delay is used between retries", + "max-retries-max-error": "Max allowed value is 100", + "max-retries-min-error": "Min allowed value is 0", + "strategy": "Strategy", + "strategy-required": "Strategy is required", + "strategy-all-hint": "Return all messages that arrived during deduplication period as a single JSON array message. Where each element represents object with msg and metadata inner properties.", + "strategy-first-hint": "Return first message that arrived during deduplication period.", + "strategy-last-hint": "Return last message that arrived during deduplication period.", + "first": "First", + "last": "Last", + "all": "All", + "output-msg-type-hint": "The message type of the deduplication result.", + "queue-name-hint": "The queue name where the deduplication result will be published.", + "keys": "Keys", + "keys-required": "Keys are required", + "rename-keys-in": "Rename keys in", + "data": "Data", + "message": "Message", + "metadata": "Metadata", + "current-key-name": "Current key name", + "key-name-required": "Key name is required", + "new-key-name": "New key name", + "new-key-name-required": "New key name is required", + "metadata-keys": "Metadata field names", + "json-path-expression": "JSON path expression", + "json-path-expression-required": "JSON path expression is required", + "json-path-expression-hint": "JSONPath specifies a path to an element or a set of elements in a JSON structure. '$' represents the root object or array.", + "relations-query": "Relations query", + "device-relations-query": "Device relations query", + "max-relation-level": "Max relation level", + "max-relation-level-error": "Value should be greater than 0 or unspecified.", + "max-relation-level-invalid": "Value should be an integer.", + "relation-type": "Relation type", + "relation-type-pattern": "Relation type pattern", + "relation-type-pattern-required": "Relation type pattern is required", + "relation-types-list": "Relation types to propagate", + "relation-types-list-hint": "If Propagate relation types are not selected, alarms will be propagated without filtering by relation type.", + "unlimited-level": "Unlimited level", + "latest-telemetry": "Latest telemetry", + "add-telemetry-key": "Add telemetry key", + "delete-from": "Delete from", + "use-regular-expression-delete-hint": "Use regular expression to delete keys by pattern.\n\nTips & tricks:\nPress 'Enter' to complete field name input.\nPress 'Backspace' to delete field name.\nMultiple field names supported.", + "fetch-into": "Fetch into", + "attr-mapping": "Attributes mapping:", + "source-attribute": "Source attribute key", + "source-attribute-required": "Source attribute key is required.", + "source-telemetry": "Source telemetry key", + "source-telemetry-required": "Source telemetry key is required.", + "target-key": "Target key", + "target-key-required": "Target key is required.", + "attr-mapping-required": "At least one mapping entry should be specified.", + "fields-mapping": "Fields mapping", + "fields-mapping-hint": "If the message field is set to $entityId, the message originator's id will be saved to the specified table column.", + "relations-query-config-direction-suffix": "originator", + "profile-name": "Profile name", + "fetch-circle-parameter-info-from-metadata-hint": "Metadata field '{{perimeterKeyName}}' should be defined in next format: {\"latitude\":48.196, \"longitude\":24.6532, \"radius\":100.0, \"radiusUnit\":\"METER\"}", + "fetch-poligon-parameter-info-from-metadata-hint": "Metadata field '{{perimeterKeyName}}' should be defined in next format: [[48.19736,24.65235],[48.19800,24.65060],...,[48.19849,24.65420]]", + "short-templatization-tooltip": "Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", + "fields-mapping-required": "At least one field mapping should be specified.", + "at-least-one-field-required": "At least one input field must have a value(s) provided.", + "originator-fields-sv-map-hint": "Target key fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", + "sv-map-hint": "Only target key fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", + "source-field": "Source field", + "source-field-required": "Source field is required.", + "originator-source": "Originator source", + "new-originator": "New originator", + "originator-customer": "Customer", + "originator-tenant": "Tenant", + "originator-related": "Related entity", + "originator-alarm-originator": "Alarm Originator", + "originator-entity": "Entity by name pattern", + "clone-message": "Clone message", + "transform": "Transform", + "default-ttl": "Default TTL in seconds", + "default-ttl-required": "Default TTL is required.", + "default-ttl-hint": "Rule node will fetch Time-to-Live (TTL) value from the message metadata. If no value is present, it defaults to the TTL specified in the configuration. If the value is set to 0, the TTL from the tenant profile configuration will be applied.", + "default-ttl-zero-hint": "TTL will not be applied if its value is set to 0.", + "min-default-ttl-message": "Only 0 minimum TTL is allowed.", + "generation-parameters": "Generation parameters", + "message-count": "Generated messages limit (0 - unlimited)", + "message-count-required": "Generated messages limit is required.", + "min-message-count-message": "Only 0 minimum message count is allowed.", + "period-seconds": "Period in seconds", + "period-seconds-required": "Period is required.", + "generation-frequency-seconds": "Generation frequency in seconds", + "generation-frequency-required": "Generation frequency is required.", + "min-generation-frequency-message": "Only 1 second minimum is allowed.", + "script-lang-tbel": "TBEL", + "script-lang-js": "JS", + "use-metadata-period-in-seconds-patterns": "Use period in seconds pattern", + "use-metadata-period-in-seconds-patterns-hint": "If selected, rule node use period in seconds interval pattern from message metadata or data assuming that intervals are in the seconds.", + "period-in-seconds-pattern": "Period in seconds pattern", + "period-in-seconds-pattern-required": "Period in seconds pattern is required", + "min-period-seconds-message": "Only 1 second minimum period is allowed.", + "originator": "Originator", + "message-body": "Message body", + "message-metadata": "Message metadata", + "generate": "Generate", + "current-rule-node": "Current Rule Node", + "current-tenant": "Current Tenant", + "generator-function": "Generator function", + "test-generator-function": "Test generator function", + "generator": "Generator", + "test-filter-function": "Test filter function", + "test-switch-function": "Test switch function", + "test-transformer-function": "Test transformer function", + "transformer": "Transformer", + "alarm-create-condition": "Alarm create condition", + "test-condition-function": "Test condition function", + "alarm-clear-condition": "Alarm clear condition", + "alarm-details-builder": "Alarm details builder", + "test-details-function": "Test details function", + "alarm-type": "Alarm type", + "select-entity-types": "Select entity types", + "alarm-type-required": "Alarm type is required.", + "alarm-severity": "Alarm severity", + "alarm-severity-required": "Alarm severity is required", + "alarm-severity-pattern": "Alarm severity pattern", + "alarm-status-filter": "Alarm status filter", + "alarm-status-list-empty": "Alarm status list is empty", + "no-alarm-status-matching": "No alarm status matching were found.", + "propagate": "Propagate alarm to related entities", + "propagate-to-owner": "Propagate alarm to entity owner (Customer or Tenant)", + "propagate-to-tenant": "Propagate alarm to Tenant", + "condition": "Condition", + "details": "Details", + "to-string": "To string", + "test-to-string-function": "Test to string function", + "from-template": "From", + "from-template-required": "From is required", + "message-to-metadata": "Message to metadata", + "metadata-to-message": "Metadata to message", + "from-message": "From message", + "from-metadata": "From metadata", + "to-template": "To", + "to-template-required": "To Template is required", + "mail-address-list-template-hint": "Comma separated address list, use ${metadataKey} for value from metadata, $[messageKey] for value from message body", + "cc-template": "Cc", + "bcc-template": "Bcc", + "subject-template": "Subject", + "subject-template-required": "Subject Template is required", + "body-template": "Body", + "body-template-required": "Body Template is required", + "dynamic-mail-body-type": "Dynamic mail body type", + "mail-body-type": "Mail body type", + "body-type-template": "Body type template", + "reply-routing-configuration": "Reply Routing Configuration", + "rpc-reply-routing-configuration-hint": "These configuration parameters specify the metadata key names used to identify the service, session, and request for sending a reply back.", + "reply-routing-configuration-hint": "These configuration parameters specify the metadata key names used to identify the service and request for sending a reply back.", + "request-id-metadata-attribute": "Request Id", + "service-id-metadata-attribute": "Service Id", + "session-id-metadata-attribute": "Session Id", + "timeout-sec": "Timeout in seconds", + "timeout-required": "Timeout is required", + "min-timeout-message": "Only 0 minimum timeout value is allowed.", + "endpoint-url-pattern": "Endpoint URL pattern", + "endpoint-url-pattern-required": "Endpoint URL pattern is required", + "request-method": "Request method", + "use-simple-client-http-factory": "Use simple client HTTP factory", + "ignore-request-body": "Without request body", + "parse-to-plain-text": "Parse to plain text", + "parse-to-plain-text-hint": "If selected, request body message payload will be transformed from JSON string to plain text, e.g. msg = \"Hello,\\t\"world\"\" will be parsed to Hello, \"world\"", + "read-timeout": "Read timeout in millis", + "read-timeout-hint": "The value of 0 means an infinite timeout", + "max-parallel-requests-count": "Max number of parallel requests", + "max-parallel-requests-count-hint": "The value of 0 specifies no limit in parallel processing", + "max-response-size": "Max response size (in KB)", + "max-response-size-hint": "The maximum amount of memory allocated for buffering data when decoding or encoding HTTP messages, such as JSON or XML payloads", + "headers": "Headers", + "headers-hint": "Use ${metadataKey} for value from metadata, $[messageKey] for value from message body in header/value fields", + "header": "Header", + "header-required": "Header is required", + "value": "Value", + "value-required": "Value is required", + "topic-pattern": "Topic pattern", + "key-pattern": "Key pattern", + "key-pattern-hint": "Optional. If a valid partition number is specified, it will be used when sending the record. If no partition is specified, the key will be used instead. If neither is specified, a partition will be assigned in a round-robin fashion.", + "topic-pattern-required": "Topic pattern is required", + "topic": "Topic", + "topic-required": "Topic is required", + "bootstrap-servers": "Bootstrap servers", + "bootstrap-servers-required": "Bootstrap servers value is required", + "other-properties": "Other properties", + "key": "Key", + "key-required": "Key is required", + "retries": "Automatically retry times if fails", + "min-retries-message": "Only 0 minimum retries is allowed.", + "batch-size-bytes": "Produces batch size in bytes", + "min-batch-size-bytes-message": "Only 0 minimum batch size is allowed.", + "linger-ms": "Time to buffer locally (ms)", + "min-linger-ms-message": "Only 0 ms minimum value is allowed.", + "buffer-memory-bytes": "Client buffer max size in bytes", + "min-buffer-memory-message": "Only 0 minimum buffer size is allowed.", + "memory-buffer-size-range": "Memory buffer size must be between 0 and {{max}} KB", + "acks": "Number of acknowledgments", + "key-serializer": "Key serializer", + "key-serializer-required": "Key serializer is required", + "value-serializer": "Value serializer", + "value-serializer-required": "Value serializer is required", + "topic-arn-pattern": "Topic ARN pattern", + "topic-arn-pattern-required": "Topic ARN pattern is required", + "aws-access-key-id": "AWS Access Key ID", + "aws-access-key-id-required": "AWS Access Key ID is required", + "aws-secret-access-key": "AWS Secret Access Key", + "aws-secret-access-key-required": "AWS Secret Access Key is required", + "aws-region": "AWS Region", + "aws-region-required": "AWS Region is required", + "exchange-name-pattern": "Exchange name pattern", + "routing-key-pattern": "Routing key pattern", + "message-properties": "Message properties", + "host": "Host", + "host-required": "Host is required", + "port": "Port", + "port-required": "Port is required", + "port-range": "Port should be in a range from 1 to 65535.", + "virtual-host": "Virtual host", + "username": "Username", + "password": "Password", + "automatic-recovery": "Automatic recovery", + "connection-timeout-ms": "Connection timeout (ms)", + "min-connection-timeout-ms-message": "Only 0 ms minimum value is allowed.", + "handshake-timeout-ms": "Handshake timeout (ms)", + "min-handshake-timeout-ms-message": "Only 0 ms minimum value is allowed.", + "client-properties": "Client properties", + "queue-url-pattern": "Queue URL pattern", + "queue-url-pattern-required": "Queue URL pattern is required", + "delay-seconds": "Delay (seconds)", + "min-delay-seconds-message": "Only 0 seconds minimum value is allowed.", + "max-delay-seconds-message": "Only 900 seconds maximum value is allowed.", + "name": "Name", + "name-required": "Name is required", + "queue-type": "Queue type", + "sqs-queue-standard": "Standard", + "sqs-queue-fifo": "FIFO", + "gcp-project-id": "GCP project ID", + "gcp-project-id-required": "GCP project ID is required", + "gcp-service-account-key": "GCP service account key file", + "gcp-service-account-key-required": "GCP service account key file is required", + "pubsub-topic-name": "Topic name", + "pubsub-topic-name-required": "Topic name is required", + "message-attributes": "Message attributes", + "message-attributes-hint": "Use ${metadataKey} for value from metadata, $[messageKey] for value from message body in name/value fields", + "connect-timeout": "Connection timeout (sec)", + "connect-timeout-required": "Connection timeout is required.", + "connect-timeout-range": "Connection timeout should be in a range from 1 to 200.", + "client-id": "Client ID", + "client-id-hint": "Optional. Leave empty for auto-generated Client ID. Be careful when specifying the Client ID. Majority of the MQTT brokers will not allow multiple connections with the same Client ID. To connect to such brokers, your mqtt Client ID must be unique. When platform is running in a micro-services mode, the copy of rule node is launched in each micro-service. This will automatically lead to multiple mqtt clients with the same ID and may cause failures of the rule node. To avoid such failures enable \"Add Service ID as suffix to Client ID\" option below.", + "append-client-id-suffix": "Add Service ID as suffix to Client ID", + "client-id-suffix-hint": "Optional. Applied when \"Client ID\" specified explicitly. If selected then Service ID will be added to Client ID as a suffix. Helps to avoid failures when platform is running in a micro-services mode.", + "device-id": "Device ID", + "device-id-required": "Device ID is required.", + "clean-session": "Clean session", + "enable-ssl": "Enable SSL", + "credentials": "Credentials", + "credentials-type": "Credentials type", + "credentials-type-required": "Credentials type is required.", + "credentials-anonymous": "Anonymous", + "credentials-basic": "Basic", + "credentials-pem": "PEM", + "credentials-pem-hint": "At least Server CA certificate file or a pair of Client certificate and Client private key files are required", + "credentials-sas": "Shared Access Signature", + "sas-key": "SAS Key", + "sas-key-required": "SAS Key is required.", + "hostname": "Hostname", + "hostname-required": "Hostname is required.", + "azure-ca-cert": "CA certificate file", + "username-required": "Username is required.", + "password-required": "Password is required.", + "ca-cert": "Server CA certificate file", + "private-key": "Client private key file", + "cert": "Client certificate file", + "no-file": "No file selected.", + "drop-file": "Drop a file or click to select a file to upload.", + "private-key-password": "Private key password", + "use-system-smtp-settings": "Use system SMTP settings", + "use-metadata-dynamic-interval": "Use dynamic interval", + "metadata-dynamic-interval-hint": "Interval start and end input fields support templatization. Note that the substituted template value should be set in milliseconds. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", + "use-metadata-interval-patterns-hint": "If selected, rule node use start and end interval patterns from message metadata or data assuming that intervals are in the milliseconds.", + "use-message-alarm-data": "Use message alarm data", + "overwrite-alarm-details": "Overwrite alarm details", + "use-alarm-severity-pattern": "Use alarm severity pattern", + "check-all-keys": "Check that all specified fields are present", + "check-all-keys-hint": "If selected, checks that all specified keys are present in the message data and metadata.", + "check-relation-to-specific-entity": "Check relation to specific entity", + "check-relation-to-specific-entity-tooltip": "If enabled, checks the presence of relation with a specific entity otherwise, checks the presence of relation with any entity. In both cases, relation lookup is based on configured direction and type.", + "check-relation-hint": "Checks existence of relation to specific entity or to any entity based on direction and relation type.", + "delete-relation-with-specific-entity": "Delete relation with specific entity", + "delete-relation-with-specific-entity-hint": "If enabled, will delete the relation with just one specific entity. Otherwise, the relation will be removed with all matching entities.", + "delete-relation-hint": "Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.", + "remove-current-relations": "Remove current relations", + "remove-current-relations-hint": "Removes current relations from the originator of the incoming message based on direction and type.", + "change-originator-to-related-entity": "Change originator to related entity", + "change-originator-to-related-entity-hint": "Used to process submitted message as a message from another entity.", + "start-interval": "Interval start", + "end-interval": "Interval end", + "start-interval-required": "Interval start is required.", + "end-interval-required": "Interval end is required.", + "smtp-protocol": "Protocol", + "smtp-host": "SMTP host", + "smtp-host-required": "SMTP host is required.", + "smtp-port": "SMTP port", + "smtp-port-required": "You must supply a smtp port.", + "smtp-port-range": "SMTP port should be in a range from 1 to 65535.", + "timeout-msec": "Timeout ms", + "min-timeout-msec-message": "Only 0 ms minimum value is allowed.", + "enter-username": "Enter username", + "enter-password": "Enter password", + "enable-tls": "Enable TLS", + "tls-version": "TLS version", + "enable-proxy": "Enable proxy", + "use-system-proxy-properties": "Use system proxy properties", + "proxy-host": "Proxy host", + "proxy-host-required": "Proxy host is required.", + "proxy-port": "Proxy port", + "proxy-port-required": "Proxy port is required.", + "proxy-port-range": "Proxy port should be in a range from 1 to 65535.", + "proxy-user": "Proxy user", + "proxy-password": "Proxy password", + "proxy-scheme": "Proxy scheme", + "numbers-to-template": "Phone Numbers To Template", + "numbers-to-template-required": "Phone Numbers To Template is required", + "numbers-to-template-hint": "Comma separated Phone Numbers, use ${metadataKey} for value from metadata, $[messageKey] for value from message body", + "sms-message-template": "SMS message Template", + "sms-message-template-required": "SMS message Template is required", + "use-system-sms-settings": "Use system SMS provider settings", + "min-period-0-seconds-message": "Only 0 second minimum period is allowed.", + "max-pending-messages": "Maximum pending messages", + "max-pending-messages-required": "Maximum pending messages is required.", + "max-pending-messages-range": "Maximum pending messages should be in a range from 1 to 100000.", + "originator-types-filter": "Originator types filter", + "interval-seconds": "Interval in seconds", + "interval-seconds-required": "Interval is required.", + "int-range": "Value must not exceed the maximum integer limit (2147483648)", + "min-interval-seconds-message": "Only 1 second minimum interval is allowed.", + "output-timeseries-key-prefix": "Output time series key prefix", + "output-timeseries-key-prefix-required": "Output time series key prefix required.", + "separator-hint": "Press \"Enter\" to complete field input.", + "select-details": "Select details", + "entity-details-id": "Id", + "entity-details-title": "Title", + "entity-details-country": "Country", + "entity-details-state": "State", + "entity-details-city": "City", + "entity-details-zip": "Zip", + "entity-details-address": "Address", + "entity-details-address2": "Address2", + "entity-details-additional_info": "Additional Info", + "entity-details-phone": "Phone", + "entity-details-email": "Email", + "email-sender": "Email sender", + "fields-to-check": "Fields to check", + "add-detail": "Add detail", + "check-all-keys-tooltip": "If enabled, checks the presence of all fields listed in the message and metadata field names within the incoming message and its metadata.", + "fields-to-check-hint": "Press \"Enter\" to complete field name input. Multiple field names supported.", + "entity-details-list-empty": "At least one detail should be selected.", + "alarm-status": "Alarm status", + "alarm-required": "At least one alarm status should be selected.", + "no-entity-details-matching": "No entity details matching were found.", + "custom-table-name": "Custom table name", + "custom-table-name-required": "Table Name is required", + "custom-table-hint": "The table must be created in your Cassandra cluster and its name must start with the prefix 'cs_tb_' to avoid the data insertion to the common TB tables. Enter the table name here without the 'cs_tb_' prefix.", + "message-field": "Message field", + "message-field-required": "Message field is required.", + "table-col": "Table column", + "table-col-required": "Table column is required.", + "latitude-field-name": "Latitude field name", + "longitude-field-name": "Longitude field name", + "latitude-field-name-required": "Latitude field name is required.", + "longitude-field-name-required": "Longitude field name is required.", + "fetch-perimeter-info-from-metadata": "Fetch perimeter information from metadata", + "fetch-perimeter-info-from-metadata-tooltip": "If perimeter type is set to 'Polygon' the value of metadata field '{{perimeterKeyName}}' will be set as perimeter definition without additional parsing of the value. Otherwise, if perimeter type is set to 'Circle' the value of '{{perimeterKeyName}}' metadata field will be parsed to extract 'latitude', 'longitude', 'radius', 'radiusUnit' fields that uses for circle perimeter definition.", + "perimeter-key-name": "Perimeter key name", + "perimeter-key-name-hint": "Metadata field name that includes perimeter information.", + "perimeter-key-name-required": "Perimeter key name is required.", + "perimeter-circle": "Circle", + "perimeter-polygon": "Polygon", + "perimeter-type": "Perimeter type", + "circle-center-latitude": "Center latitude", + "circle-center-latitude-required": "Center latitude is required.", + "circle-center-longitude": "Center longitude", + "circle-center-longitude-required": "Center longitude is required.", + "range-unit-meter": "Meter", + "range-unit-kilometer": "Kilometer", + "range-unit-foot": "Foot", + "range-unit-mile": "Mile", + "range-unit-nautical-mile": "Nautical mile", + "range-units": "Range units", + "range-units-required": "Range units is required.", + "range": "Range", + "range-required": "Range is required.", + "polygon-definition": "Polygon definition", + "polygon-definition-required": "Polygon definition is required.", + "polygon-definition-hint": "Use the following format for manual definition of polygon: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].", + "min-inside-duration": "Minimal inside duration", + "min-inside-duration-value-required": "Minimal inside duration is required", + "min-inside-duration-time-unit": "Minimal inside duration time unit", + "min-outside-duration": "Minimal outside duration", + "min-outside-duration-value-required": "Minimal outside duration is required", + "min-outside-duration-time-unit": "Minimal outside duration time unit", + "tell-failure-if-absent": "Tell Failure", + "tell-failure-if-absent-hint": "If at least one selected key doesn't exist the outbound message will report \"Failure\".", + "get-latest-value-with-ts": "Fetch timestamp for the latest telemetry values", + "get-latest-value-with-ts-hint": "If selected, the latest telemetry values will also include timestamp, e.g: \"temp\": \"{\"ts\":1574329385897, \"value\":42}\"", + "ignore-null-strings": "Ignore null strings", + "ignore-null-strings-hint": "If selected rule node will ignore entity fields with empty value.", + "add-metadata-key-values-as-kafka-headers": "Add Message metadata key-value pairs to Kafka record headers", + "add-metadata-key-values-as-kafka-headers-hint": "If selected, key-value pairs from message metadata will be added to the outgoing records headers as byte arrays with predefined charset encoding.", + "charset-encoding": "Charset encoding", + "charset-encoding-required": "Charset encoding is required.", + "charset-us-ascii": "US-ASCII", + "charset-iso-8859-1": "ISO-8859-1", + "charset-utf-8": "UTF-8", + "charset-utf-16be": "UTF-16BE", + "charset-utf-16le": "UTF-16LE", + "charset-utf-16": "UTF-16", + "select-queue-hint": "The queue name can be selected from a drop-down list or add a custom name.", + "device-profile-node-hint": "Useful if you have duration or repeating conditions to ensure continuity of alarm state evaluation.", + "persist-alarm-rules": "Persist state of alarm rules", + "persist-alarm-rules-hint": "If enabled, the rule node will store the state of processing to the database.", + "fetch-alarm-rules": "Fetch state of alarm rules", + "fetch-alarm-rules-hint": "If enabled, the rule node will restore the state of processing on initialization and ensure that alarms are raised even after server restarts. Otherwise, the state will be restored when the first message from the device arrives.", + "input-value-key": "Input value key", + "input-value-key-required": "Input value key is required.", + "output-value-key": "Output value key", + "output-value-key-required": "Output value key is required.", + "number-of-digits-after-floating-point": "Number of digits after floating point", + "number-of-digits-after-floating-point-range": "Number of digits after floating point should be in a range from 0 to 15.", + "failure-if-delta-negative": "Tell Failure if delta is negative", + "failure-if-delta-negative-tooltip": "Rule node forces failure of message processing if delta value is negative.", + "use-caching": "Use caching", + "use-caching-tooltip": "Rule node will cache the value of \"{{inputValueKey}}\" that arrives from the incoming message to improve performance. Note that the cache will not be updated if you modify the \"{{inputValueKey}}\" value elsewhere.", + "add-time-difference-between-readings": "Add the time difference between \"{{inputValueKey}}\" readings", + "add-time-difference-between-readings-tooltip": "If enabled, the rule node will add the \"{{periodValueKey}}\" to the outbound message.", + "period-value-key": "Period value key", + "period-value-key-required": "Period value key is required.", + "general-pattern-hint": "Use ${metadataKey} for value from metadata, $[messageKey] for value from message body.", + "alarm-severity-pattern-hint": "Use ${metadataKey} for value from metadata, $[messageKey] for value from message body. Alarm severity should be system (CRITICAL, MAJOR etc.)", + "output-node-name-hint": "The 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.", + "skip-latest-persistence": "Skip latest persistence", + "skip-latest-persistence-hint": "Rule node will not update values for incoming keys for the latest time series data. Useful for highly loaded use-cases to decrease the pressure on the DB.", + "use-server-ts": "Use server ts", + "use-server-ts-hint": "Rule node will use the timestamp of message processing instead of the timestamp from the message. Useful for all sorts of sequential processing if you merge messages from multiple sources (devices, assets, etc).", + "kv-map-pattern-hint": "All input fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", + "kv-map-single-pattern-hint": "Input field support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", + "shared-scope": "Shared scope", + "server-scope": "Server scope", + "client-scope": "Client scope", + "attribute-type": "Attribute", + "constant-type": "Constant", + "time-series-type": "Time series", + "message-body-type": "Message", + "message-metadata-type": "Metadata", + "argument-tile": "Arguments", + "no-arguments-prompt": "No arguments configured", + "result-title": "Result", + "functions-field-input": "Functions", + "no-option-found": "No option found", + "argument-source-field-input": "Source", + "argument-source-field-input-required": "Argument source is required.", + "argument-key-field-input": "Key", + "argument-key-field-input-required": "Argument key is required.", + "constant-value-field-input": "Constant value", + "constant-value-field-input-required": "Constant value is required.", + "attribute-scope-field-input": "Attribute scope", + "attribute-scope-field-input-required": "Attribute scope os required.", + "default-value-field-input": "Default value", + "type-field-input": "Type", + "type-field-input-required": "Type is required.", + "key-field-input": "Key", + "add-entity-type": "Add entity type", + "add-device-profile": "Add device profile", + "key-field-input-required": "Key is required.", + "number-floating-point-field-input": "Number of digits after floating point", + "number-floating-point-field-input-hint": "Use 0 to convert result to integer", + "add-to-message-field-input": "Add to message", + "add-to-metadata-field-input": "Add to metadata", + "custom-expression-field-input": "Mathematical Expression", + "custom-expression-field-input-required": "Mathematical expression is required", + "custom-expression-field-input-hint": "Specify a mathematical expression to evaluate. Default expression demonstrates how to transform Fahrenheit to Celsius", + "retained-message": "Retained", + "attributes-mapping": "Attributes mapping", + "latest-telemetry-mapping": "Latest telemetry mapping", + "add-mapped-attribute-to": "Add mapped attributes to", + "add-mapped-latest-telemetry-to": "Add mapped latest telemetry to", + "add-mapped-fields-to": "Add mapped fields to", + "add-selected-details-to": "Add selected details to", + "clear-selected-types": "Clear selected types", + "clear-selected-details": "Clear selected details", + "clear-selected-fields": "Clear selected fields", + "clear-selected-keys": "Clear selected keys", + "geofence-configuration": "Geofence configuration", + "coordinate-field-names": "Coordinate field names", + "coordinate-field-hint": "Rule node tries to retrieve the specified fields from the message. If they are not present, it will look them up in the metadata.", + "presence-monitoring-strategy": "Presence monitoring strategy", + "presence-monitoring-strategy-on-first-message": "On first message", + "presence-monitoring-strategy-on-each-message": "On each message", + "presence-monitoring-strategy-on-first-message-hint": "Reports presence status 'Inside' or 'Outside' on the first message after the configured minimal duration has passed since previous presence status 'Entered' or 'Left' update.", + "presence-monitoring-strategy-on-each-message-hint": "Reports presence status 'Inside' or 'Outside' on each message after presence status 'Entered' or 'Left' update.", + "fetch-credentials-to": "Fetch credentials to", + "add-originator-attributes-to": "Add originator attributes to", + "originator-attributes": "Originator attributes", + "fetch-latest-telemetry-with-timestamp": "Fetch latest telemetry with timestamp", + "fetch-latest-telemetry-with-timestamp-tooltip": "If selected, latest telemetry values will be added to the outbound metadata with timestamp, e.g: \"{{latestTsKeyName}}\": \"{\"ts\":1574329385897, \"value\":42}\"", + "tell-failure": "Tell failure if any of the attributes are missing", + "tell-failure-tooltip": "If at least one selected key doesn't exist the outbound message will report \"Failure\".", + "created-time": "Created time", + "chip-help": "Press 'Enter' to complete {{inputName}} input. \nPress 'Backspace' to delete {{inputName}}. \nMultiple values supported.", + "detail": "detail", + "field-name": "field name", + "device-profile": "device profile", + "entity-type": "entity type", + "message-type": "message type", + "timeseries-key": "time series key", + "type": "Type", + "first-name": "First name", + "last-name": "Last name", + "label": "Label", + "originator-fields-mapping": "Originator fields mapping", + "add-mapped-originator-fields-to": "Add mapped originator fields to", + "fields": "Fields", + "skip-empty-fields": "Skip empty fields", + "skip-empty-fields-tooltip": "Fields with empty values will not be added to the output message/output metadata.", + "fetch-interval": "Fetch interval", + "fetch-strategy": "Fetch strategy", + "fetch-timeseries-from-to": "Fetch time series from {{startInterval}} {{startIntervalTimeUnit}} ago to {{endInterval}} {{endIntervalTimeUnit}} ago.", + "fetch-timeseries-from-to-invalid": "Fetch time series invalid (\"Interval start\" should be less than \"Interval end\").", + "use-metadata-dynamic-interval-tooltip": "If selected, the rule node will use dynamic interval start and end based on the message and metadata patterns.", + "all-mode-hint": "If selected fetch mode \"All\" rule node will retrieve telemetry from the fetch interval with configurable query parameters.", + "first-mode-hint": "If selected fetch mode \"First\" rule node will retrieve the closest telemetry to the fetch interval's start.", + "last-mode-hint": "If selected fetch mode \"Last\" rule node will retrieve the closest telemetry to the fetch interval's end.", + "ascending": "Ascending", + "descending": "Descending", + "min": "Min", + "max": "Max", + "average": "Average", + "sum": "Sum", + "count": "Count", + "none": "None", + "last-level-relation-tooltip": "If selected, the rule node will search related entities only on the level set in the max relation level.", + "last-level-device-relation-tooltip": "If selected, the rule node will search related devices only on the level set in the max relation level.", + "data-to-fetch": "Data to fetch", + "mapping-of-customers": "Mapping of customer's", + "map-fields-required": "All mapping fields are required.", + "attributes": "Attributes", + "related-device-attributes": "Related device attributes", + "add-selected-attributes-to": "Add selected attributes to", + "device-profiles": "Device profiles", + "mapping-of-tenant": "Mapping of tenant's", + "add-attribute-key": "Add attribute key", + "message-template": "Message template", + "message-template-required": "Message template is required", + "use-system-slack-settings": "Use system slack settings", + "slack-api-token": "Slack API token", + "slack-api-token-required": "Slack API token is required", + "keys-mapping": "keys mapping", + "add-key": "Add key", + "recipients": "Recipients", + "message-subject-and-content": "Message subject and content", + "template-rules-hint": "Both input fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the message metadata.", + "originator-customer-desc": "Use customer of incoming message originator as new originator.", + "originator-tenant-desc": "Use current tenant as new originator.", + "originator-related-entity-desc": "Use related entity as new originator. Lookup based on configured relation type and direction.", + "originator-alarm-originator-desc": "Use alarm originator as new originator. Only if incoming message originator is alarm entity.", + "originator-entity-by-name-pattern-desc": "Use entity fetched from DB as new originator. Lookup based on entity type and specified name pattern.", + "email-from-template-hint": "Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", + "recipients-block-main-hint": "Comma-separated address list. All input fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", + "forward-msg-default-rule-chain": "Forward message to the originator's default rule chain", + "forward-msg-default-rule-chain-tooltip": "If enabled, message will be forwarded to the originator's default rule chain, or rule chain from configuration, if originator has no default rule chain defined in the entity profile.", + "exclude-zero-deltas": "Exclude zero deltas from outbound message", + "exclude-zero-deltas-hint": "If enabled, the \"{{outputValueKey}}\" output key will be added to the outbound message if its value is not zero.", + "exclude-zero-deltas-time-difference-hint": "If enabled, the \"{{outputValueKey}}\" and \"{{periodValueKey}}\" output keys will be added to the outbound message only if the \"{{outputValueKey}}\" value is not zero.", + "search-direction-from": "From originator to target entity", + "search-direction-to": "From target entity to originator", + "del-relation-direction-from": "From originator", + "del-relation-direction-to": "To originator", + "target-entity": "Target entity", + "function-configuration": "Function configuration", + "function-name": "Function name", + "function-name-required": "Function name is required.", + "qualifier": "Qualifier", + "qualifier-hint": "If the qualifier is not specified, the default qualifier \"$LATEST\" will be used.", + "aws-credentials": "AWS Credentials", + "connection-timeout": "Connection timeout", + "connection-timeout-required": "Connection timeout is required.", + "connection-timeout-min": "Min connection timeout is 0.", + "connection-timeout-hint": "The amount of time to wait in seconds when initially establishing a connection before giving up and timing out. A value of 0 means infinity, and is not recommended.", + "request-timeout": "Request timeout", + "request-timeout-required": "Request timeout is required", + "request-timeout-min": "Min request timeout is 0", + "request-timeout-hint": "The amount of time to wait in seconds for the request to complete before giving up and timing out. A value of 0 means infinity, and is not recommended.", + "tell-failure-aws-lambda": "Tell Failure if AWS Lambda function execution raises exception", + "tell-failure-aws-lambda-hint": "Rule node forces failure of message processing if AWS Lambda function execution raises exception." + }, + "key-val": { + "key": "Key", + "value": "Value", + "see-examples": "See examples.", + "remove-entry": "Remove entry", + "remove-mapping-entry": "Remove mapping entry", + "add-mapping-entry": "Add mapping", + "add-entry": "Add entry", + "copy-key-values-from": "Copy key-values from", + "delete-key-values": "Delete key-values", + "delete-key-values-from": "Delete key-values from", + "at-least-one-key-error": "At least one key should be selected.", + "unique-key-value-pair-error": "'{{keyText}}' must be different from the '{{valText}}'!" + }, + "mail-body-type": { + "plain-text": "Plain text", + "html": "HTML", + "dynamic": "Dynamic", + "use-body-type-template": "Use body type template", + "plain-text-description": "Simple, unformatted text with no special styling or formating.", + "html-text-description": "Allows you to use HTML tags for formatting, links and images in your mai body.", + "dynamic-text-description": "Allows to use Plain Text or HTML body type dynamically based on templatization feature.", + "after-template-evaluation-hint": "After template evaluation value should be true for HTML, and false for Plain text." + } + }, "tenant": { "tenant": "Tenant", "tenants": "Tenants", From c82659bd957701732ee72040cbdbfb03b3cde137 Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 8 Jan 2025 11:50:36 +0200 Subject: [PATCH 043/108] fix bug test: lwm2m profile without params --- .../org/thingsboard/server/edge/DeviceProfileEdgeTest.java | 2 +- .../transport/lwm2m/AbstractLwM2MIntegrationTest.java | 6 +++--- .../transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/edge/DeviceProfileEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/DeviceProfileEdgeTest.java index a085f3a821..1202c764d7 100644 --- a/application/src/test/java/org/thingsboard/server/edge/DeviceProfileEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/DeviceProfileEdgeTest.java @@ -359,7 +359,7 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest { transportConfiguration.setBootstrapServerUpdateEnable(true); TelemetryMappingConfiguration observeAttrConfiguration = - JacksonUtil.fromString(AbstractLwM2MIntegrationTest.OBSERVE_ATTRIBUTES_WITH_PARAMS, TelemetryMappingConfiguration.class); + JacksonUtil.fromString(AbstractLwM2MIntegrationTest.TELEMETRY_WITHOUT_OBSERVE, TelemetryMappingConfiguration.class); transportConfiguration.setObserveAttr(observeAttrConfiguration); List bootstrap = new ArrayList<>(); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index 065c4a2d26..2357ef01f5 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java @@ -147,16 +147,16 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte " \"telemetry\": [],\n" + " \"attributeLwm2m\": {}\n" + " }"; - public static String OBSERVE_ATTRIBUTES_WITH_PARAMS = + public static String TELEMETRY_WITHOUT_OBSERVE = " {\n" + " \"keyName\": {\n" + " \"/3_1.2/0/9\": \"batteryLevel\"\n" + " },\n" + " \"observe\": [],\n" + " \"attribute\": [\n" + - " \"/3_1.2/0/9\"\n" + " ],\n" + " \"telemetry\": [\n" + + " \"/3_1.2/0/9\"\n" + " ],\n" + " \"attributeLwm2m\": {}\n" + " }"; @@ -249,7 +249,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte LwM2MDeviceCredentials deviceCredentials, String endpoint, boolean queueMode) throws Exception { - Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITH_PARAMS, getBootstrapServerCredentialsNoSec(NONE)); + Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(TELEMETRY_WITHOUT_OBSERVE, getBootstrapServerCredentialsNoSec(NONE)); DeviceProfile deviceProfile = createLwm2mDeviceProfile("profileFor" + endpoint, transportConfiguration); Device device = createLwm2mDevice(deviceCredentials, endpoint, deviceProfile.getId()); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java index a50f7df6a2..4ed06b99f2 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java @@ -51,7 +51,7 @@ public class Ota5LwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest { @Test public void testFirmwareUpdateWithClientWithoutFirmwareOtaInfoFromProfile_IsNotSupported() throws Exception { - Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITH_PARAMS, getBootstrapServerCredentialsNoSec(NONE)); + Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(TELEMETRY_WITHOUT_OBSERVE, getBootstrapServerCredentialsNoSec(NONE)); DeviceProfile deviceProfile = createLwm2mDeviceProfile("profileFor" + this.CLIENT_ENDPOINT_WITHOUT_FW_INFO, transportConfiguration); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(this.CLIENT_ENDPOINT_WITHOUT_FW_INFO)); final Device device = createLwm2mDevice(deviceCredentials, this.CLIENT_ENDPOINT_WITHOUT_FW_INFO, deviceProfile.getId()); From 038dea7810f12fee60fc5186a3d2e69a5426b218 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 8 Jan 2025 15:08:13 +0200 Subject: [PATCH 044/108] UI: Rename rule node modules and component --- .../deduplication/TbMsgDeduplicationNode.java | 2 +- .../engine/profile/TbDeviceProfileNode.java | 2 +- ...e.ts => action-rule-node-config.module.ts} | 11 ++-- .../action/device-profile-config.component.ts | 2 +- .../action/device-state-config.component.ts | 7 ++- ...e.ts => common-rule-node-config.module.ts} | 2 +- ...ts => enrichment-rule-node-core.module.ts} | 8 +-- ...ts => external-rule-node-config.module.ts} | 8 +-- ...e.ts => filter-rule-node-config.module.ts} | 8 +-- .../filter/message-type-config.component.ts | 3 +- ...ule.ts => flow-rule-node-config.module.ts} | 4 +- .../rule-node/rule-node-config.module.ts | 60 +++++++++---------- .../change-originator-config.component.html | 2 +- .../change-originator-config.component.ts | 0 .../copy-keys-config.component.html | 0 .../copy-keys-config.component.ts | 0 .../deduplication-config.component.html | 0 .../deduplication-config.component.ts | 2 +- .../delete-keys-config.component.html | 0 .../delete-keys-config.component.ts | 0 .../node-json-path-config.component.html | 0 .../node-json-path-config.component.ts | 0 .../rename-keys-config.component.html | 0 .../rename-keys-config.component.scss | 0 .../rename-keys-config.component.ts | 0 .../script-config.component.html | 0 .../script-config.component.ts | 0 .../to-email-config.component.html | 0 .../to-email-config.component.scss | 0 .../to-email-config.component.ts | 0 ...transformation-rule-node-config.module.ts} | 10 ++-- .../assets/locale/locale.constant-en_US.json | 2 +- 32 files changed, 68 insertions(+), 65 deletions(-) rename ui-ngx/src/app/modules/home/components/rule-node/action/{rule-node-config-action.module.ts => action-rule-node-config.module.ts} (93%) rename ui-ngx/src/app/modules/home/components/rule-node/common/{rule-node-config-common.module.ts => common-rule-node-config.module.ts} (98%) rename ui-ngx/src/app/modules/home/components/rule-node/enrichment/{rule-node-core-enrichment.module.ts => enrichment-rule-node-core.module.ts} (93%) rename ui-ngx/src/app/modules/home/components/rule-node/external/{rule-node-config-external.module.ts => external-rule-node-config.module.ts} (93%) rename ui-ngx/src/app/modules/home/components/rule-node/filter/{rule-node-config-filter.module.ts => filter-rule-node-config.module.ts} (92%) rename ui-ngx/src/app/modules/home/components/rule-node/flow/{rule-node-config-flow.module.ts => flow-rule-node-config.module.ts} (92%) rename ui-ngx/src/app/modules/home/components/rule-node/{transform => transformation}/change-originator-config.component.html (98%) rename ui-ngx/src/app/modules/home/components/rule-node/{transform => transformation}/change-originator-config.component.ts (100%) rename ui-ngx/src/app/modules/home/components/rule-node/{transform => transformation}/copy-keys-config.component.html (100%) rename ui-ngx/src/app/modules/home/components/rule-node/{transform => transformation}/copy-keys-config.component.ts (100%) rename ui-ngx/src/app/modules/home/components/rule-node/{transform => transformation}/deduplication-config.component.html (100%) rename ui-ngx/src/app/modules/home/components/rule-node/{transform => transformation}/deduplication-config.component.ts (98%) rename ui-ngx/src/app/modules/home/components/rule-node/{transform => transformation}/delete-keys-config.component.html (100%) rename ui-ngx/src/app/modules/home/components/rule-node/{transform => transformation}/delete-keys-config.component.ts (100%) rename ui-ngx/src/app/modules/home/components/rule-node/{transform => transformation}/node-json-path-config.component.html (100%) rename ui-ngx/src/app/modules/home/components/rule-node/{transform => transformation}/node-json-path-config.component.ts (100%) rename ui-ngx/src/app/modules/home/components/rule-node/{transform => transformation}/rename-keys-config.component.html (100%) rename ui-ngx/src/app/modules/home/components/rule-node/{transform => transformation}/rename-keys-config.component.scss (100%) rename ui-ngx/src/app/modules/home/components/rule-node/{transform => transformation}/rename-keys-config.component.ts (100%) rename ui-ngx/src/app/modules/home/components/rule-node/{transform => transformation}/script-config.component.html (100%) rename ui-ngx/src/app/modules/home/components/rule-node/{transform => transformation}/script-config.component.ts (100%) rename ui-ngx/src/app/modules/home/components/rule-node/{transform => transformation}/to-email-config.component.html (100%) rename ui-ngx/src/app/modules/home/components/rule-node/{transform => transformation}/to-email-config.component.scss (100%) rename ui-ngx/src/app/modules/home/components/rule-node/{transform => transformation}/to-email-config.component.ts (100%) rename ui-ngx/src/app/modules/home/components/rule-node/{transform/rule-node-config-transform.module.ts => transformation/transformation-rule-node-config.module.ts} (88%) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java index 891efec861..6d13332f6f 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java @@ -59,7 +59,7 @@ import static org.thingsboard.server.common.data.DataConstants.QUEUE_NAME; "
  • ALL - return all messages as a single JSON array message. " + "Where each element represents object with msg and metadata inner properties.
  • ", icon = "content_copy", - configDirective = "tbActionNodeMsgDeduplicationConfig" + configDirective = "tbTransformationNodeDeduplicationConfig" ) @Slf4j public class TbMsgDeduplicationNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java index 9df674cf88..40fa08ae3c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java @@ -59,7 +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.", - configDirective = "tbDeviceProfileConfig" + configDirective = "tbActionNodeDeviceProfileConfig" ) public class TbDeviceProfileNode implements TbNode { diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/rule-node-config-action.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/action-rule-node-config.module.ts similarity index 93% rename from ui-ngx/src/app/modules/home/components/rule-node/action/rule-node-config-action.module.ts rename to ui-ngx/src/app/modules/home/components/rule-node/action/action-rule-node-config.module.ts index 8c8350378d..3e78ee4c46 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/rule-node-config-action.module.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/action-rule-node-config.module.ts @@ -33,7 +33,7 @@ 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 { RuleNodeConfigCommonModule } from '../common/rule-node-config-common.module'; +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'; @@ -42,7 +42,6 @@ import { DeleteAttributesConfigComponent } from './delete-attributes-config.comp import { MathFunctionConfigComponent } from './math-function-config.component'; import { DeviceStateConfigComponent } from './device-state-config.component'; import { SendRestApiCallReplyConfigComponent } from './send-rest-api-call-reply-config.component'; -import { EmptyConfigComponent } from '@home/components/rule-node/empty-config.component'; @NgModule({ declarations: [ @@ -74,7 +73,7 @@ import { EmptyConfigComponent } from '@home/components/rule-node/empty-config.co CommonModule, SharedModule, HomeComponentsModule, - RuleNodeConfigCommonModule + CommonRuleNodeConfigModule ], exports: [ DeleteAttributesConfigComponent, @@ -102,10 +101,10 @@ import { EmptyConfigComponent } from '@home/components/rule-node/empty-config.co DeviceStateConfigComponent ] }) -export class RuleNodeConfigActionModule { +export class ActionRuleNodeConfigModule { } -export const ruleNodeActionConfigComponentsMap: Record> = { +export const actionRuleNodeConfigComponentsMap: Record> = { 'tbActionNodeAssignToCustomerConfig': AssignCustomerConfigComponent, 'tbActionNodeAttributesConfig': AttributesConfigComponent, 'tbActionNodeClearAlarmConfig': ClearAlarmConfigComponent, @@ -113,7 +112,7 @@ export const ruleNodeActionConfigComponentsMap: Record> = { +export const enrichmentRuleNodeConfigComponentsMap: Record> = { 'tbEnrichmentNodeCalculateDeltaConfig': CalculateDeltaConfigComponent, 'tbEnrichmentNodeCustomerAttributesConfig': CustomerAttributesConfigComponent, 'tbEnrichmentNodeDeviceAttributesConfig': DeviceAttributesConfigComponent, diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/rule-node-config-external.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/external/external-rule-node-config.module.ts similarity index 93% rename from ui-ngx/src/app/modules/home/components/rule-node/external/rule-node-config-external.module.ts rename to ui-ngx/src/app/modules/home/components/rule-node/external/external-rule-node-config.module.ts index c170d9ccd6..38395b9dac 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/external/rule-node-config-external.module.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/external-rule-node-config.module.ts @@ -29,7 +29,7 @@ import { SendSmsConfigComponent } from './send-sms-config.component'; import { CommonModule } from '@angular/common'; import { IRuleNodeConfigurationComponent, SharedModule } from '@shared/public-api'; import { HomeComponentsModule } from '@home/components/public-api'; -import { RuleNodeConfigCommonModule } from '../common/rule-node-config-common.module'; +import { CommonRuleNodeConfigModule } from '../common/common-rule-node-config.module'; import { SlackConfigComponent } from './slack-config.component'; import { LambdaConfigComponent } from './lambda-config.component'; @@ -53,7 +53,7 @@ import { LambdaConfigComponent } from './lambda-config.component'; CommonModule, SharedModule, HomeComponentsModule, - RuleNodeConfigCommonModule + CommonRuleNodeConfigModule ], exports: [ SnsConfigComponent, @@ -71,10 +71,10 @@ import { LambdaConfigComponent } from './lambda-config.component'; SlackConfigComponent ] }) -export class RuleNodeConfigExternalModule { +export class ExternalRuleNodeConfigModule { } -export const ruleNodeExternalConfigComponentsMap: Record> = { +export const externalRuleNodeConfigComponentsMap: Record> = { 'tbExternalNodeAzureIotHubConfig': AzureIotHubConfigComponent, 'tbExternalNodeKafkaConfig': KafkaConfigComponent, 'tbExternalNodeLambdaConfig': LambdaConfigComponent, diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/rule-node-config-filter.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/filter/filter-rule-node-config.module.ts similarity index 92% rename from ui-ngx/src/app/modules/home/components/rule-node/filter/rule-node-config-filter.module.ts rename to ui-ngx/src/app/modules/home/components/rule-node/filter/filter-rule-node-config.module.ts index 52ad3169d5..ca0aaffd71 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/filter/rule-node-config-filter.module.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/filter-rule-node-config.module.ts @@ -25,7 +25,7 @@ import { OriginatorTypeConfigComponent } from './originator-type-config.componen import { ScriptConfigComponent } from './script-config.component'; import { SwitchConfigComponent } from './switch-config.component'; import { CheckAlarmStatusComponent } from './check-alarm-status.component'; -import { RuleNodeConfigCommonModule } from '../common/rule-node-config-common.module'; +import { CommonRuleNodeConfigModule } from '../common/common-rule-node-config.module'; @NgModule({ declarations: [ @@ -41,7 +41,7 @@ import { RuleNodeConfigCommonModule } from '../common/rule-node-config-common.mo imports: [ CommonModule, SharedModule, - RuleNodeConfigCommonModule + CommonRuleNodeConfigModule ], exports: [ CheckMessageConfigComponent, @@ -54,10 +54,10 @@ import { RuleNodeConfigCommonModule } from '../common/rule-node-config-common.mo CheckAlarmStatusComponent ] }) -export class RuleNodeConfigFilterModule { +export class FilterRuleNodeConfigModule { } -export const ruleNodeFilterConfigComponentsMap: Record> = { +export const filterRuleNodeConfigComponentsMap: Record> = { 'tbFilterNodeCheckAlarmStatusConfig': CheckAlarmStatusComponent, 'tbFilterNodeCheckMessageConfig': CheckMessageConfigComponent, 'tbFilterNodeCheckRelationConfig': CheckRelationConfigComponent, diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/message-type-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/filter/message-type-config.component.ts index 0f8392c4f0..61cc018aab 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/filter/message-type-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/message-type-config.component.ts @@ -15,8 +15,7 @@ /// import { Component } from '@angular/core'; -import { AppState, isDefinedAndNotNull } from '@core/public-api'; -import { Store } from '@ngrx/store'; +import { isDefinedAndNotNull } from '@core/public-api'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; diff --git a/ui-ngx/src/app/modules/home/components/rule-node/flow/rule-node-config-flow.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/flow/flow-rule-node-config.module.ts similarity index 92% rename from ui-ngx/src/app/modules/home/components/rule-node/flow/rule-node-config-flow.module.ts rename to ui-ngx/src/app/modules/home/components/rule-node/flow/flow-rule-node-config.module.ts index 4dae3212aa..2ee36f71fc 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/flow/rule-node-config-flow.module.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/flow/flow-rule-node-config.module.ts @@ -34,10 +34,10 @@ import { RuleChainOutputComponent } from './rule-chain-output.component'; RuleChainOutputComponent ] }) -export class RuleNodeConfigFlowModule { +export class FlowRuleNodeConfigModule { } -export const ruleNodeFlowConfigComponentsMap: Record> = { +export const flowRuleNodeConfigComponentsMap: Record> = { 'tbFlowNodeRuleChainInputConfig': RuleChainInputComponent, 'tbFlowNodeRuleChainOutputConfig': RuleChainOutputComponent } diff --git a/ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.module.ts index 79d46f02d5..58efd925db 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.module.ts @@ -19,29 +19,29 @@ import { EmptyConfigComponent } from './empty-config.component'; import { CommonModule } from '@angular/common'; import { SharedModule } from '@shared/shared.module'; import { - ruleNodeActionConfigComponentsMap, - RuleNodeConfigActionModule -} from '@home/components/rule-node/action/rule-node-config-action.module'; + actionRuleNodeConfigComponentsMap, + ActionRuleNodeConfigModule +} from '@home/components/rule-node/action/action-rule-node-config.module'; import { - RuleNodeConfigFilterModule, - ruleNodeFilterConfigComponentsMap -} from '@home/components/rule-node/filter/rule-node-config-filter.module'; + filterRuleNodeConfigComponentsMap, + FilterRuleNodeConfigModule +} from '@home/components/rule-node/filter/filter-rule-node-config.module'; import { - RuleNodeCoreEnrichmentModule, - ruleNodeEnrichmentConfigComponentsMap -} from '@home/components/rule-node/enrichment/rule-node-core-enrichment.module'; + enrichmentRuleNodeConfigComponentsMap, + EnrichmentRuleNodeCoreModule +} from '@home/components/rule-node/enrichment/enrichment-rule-node-core.module'; import { - RuleNodeConfigExternalModule, - ruleNodeExternalConfigComponentsMap -} from '@home/components/rule-node/external/rule-node-config-external.module'; + externalRuleNodeConfigComponentsMap, + ExternalRuleNodeConfigModule +} from '@home/components/rule-node/external/external-rule-node-config.module'; import { - RuleNodeConfigTransformModule, - ruleNodeTransformConfigComponentsMap -} from '@home/components/rule-node/transform/rule-node-config-transform.module'; + transformationRuleNodeConfigComponentsMap, + TransformationRuleNodeConfigModule +} from '@home/components/rule-node/transformation/transformation-rule-node-config.module'; import { - RuleNodeConfigFlowModule, - ruleNodeFlowConfigComponentsMap -} from '@home/components/rule-node/flow/rule-node-config-flow.module'; + flowRuleNodeConfigComponentsMap, + FlowRuleNodeConfigModule +} from '@home/components/rule-node/flow/flow-rule-node-config.module'; import { IRuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; @NgModule({ @@ -53,23 +53,23 @@ import { IRuleNodeConfigurationComponent } from '@shared/models/rule-node.models SharedModule ], exports: [ - RuleNodeConfigActionModule, - RuleNodeConfigFilterModule, - RuleNodeCoreEnrichmentModule, - RuleNodeConfigExternalModule, - RuleNodeConfigTransformModule, - RuleNodeConfigFlowModule, + ActionRuleNodeConfigModule, + FilterRuleNodeConfigModule, + EnrichmentRuleNodeCoreModule, + ExternalRuleNodeConfigModule, + TransformationRuleNodeConfigModule, + FlowRuleNodeConfigModule, EmptyConfigComponent ] }) export class RuleNodeConfigModule {} export const ruleNodeConfigComponentsMap: Record> = { - ...ruleNodeActionConfigComponentsMap, - ...ruleNodeEnrichmentConfigComponentsMap, - ...ruleNodeExternalConfigComponentsMap, - ...ruleNodeFilterConfigComponentsMap, - ...ruleNodeFlowConfigComponentsMap, - ...ruleNodeTransformConfigComponentsMap, + ...actionRuleNodeConfigComponentsMap, + ...enrichmentRuleNodeConfigComponentsMap, + ...externalRuleNodeConfigComponentsMap, + ...filterRuleNodeConfigComponentsMap, + ...flowRuleNodeConfigComponentsMap, + ...transformationRuleNodeConfigComponentsMap, 'tbNodeEmptyConfig': EmptyConfigComponent }; diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/change-originator-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transformation/change-originator-config.component.html similarity index 98% rename from ui-ngx/src/app/modules/home/components/rule-node/transform/change-originator-config.component.html rename to ui-ngx/src/app/modules/home/components/rule-node/transformation/change-originator-config.component.html index 818ea5a12d..1f66108e48 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/transform/change-originator-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/transformation/change-originator-config.component.html @@ -36,7 +36,7 @@
    + *ngIf="changeOriginatorConfigForm.get('originatorSource').value === originatorSource.ENTITY"> diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/change-originator-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/transformation/change-originator-config.component.ts similarity index 100% rename from ui-ngx/src/app/modules/home/components/rule-node/transform/change-originator-config.component.ts rename to ui-ngx/src/app/modules/home/components/rule-node/transformation/change-originator-config.component.ts diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/copy-keys-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transformation/copy-keys-config.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/rule-node/transform/copy-keys-config.component.html rename to ui-ngx/src/app/modules/home/components/rule-node/transformation/copy-keys-config.component.html diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/copy-keys-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/transformation/copy-keys-config.component.ts similarity index 100% rename from ui-ngx/src/app/modules/home/components/rule-node/transform/copy-keys-config.component.ts rename to ui-ngx/src/app/modules/home/components/rule-node/transformation/copy-keys-config.component.ts diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/deduplication-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transformation/deduplication-config.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/rule-node/transform/deduplication-config.component.html rename to ui-ngx/src/app/modules/home/components/rule-node/transformation/deduplication-config.component.html diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/deduplication-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/transformation/deduplication-config.component.ts similarity index 98% rename from ui-ngx/src/app/modules/home/components/rule-node/transform/deduplication-config.component.ts rename to ui-ngx/src/app/modules/home/components/rule-node/transformation/deduplication-config.component.ts index ec9b3a9cf2..bf50b36f3a 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/transform/deduplication-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/transformation/deduplication-config.component.ts @@ -21,7 +21,7 @@ import { deduplicationStrategiesTranslations, FetchMode } from '@home/components import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; @Component({ - selector: 'tb-action-node-msg-deduplication-config', + selector: 'tb-transformation-node-deduplication-config', templateUrl: './deduplication-config.component.html', styleUrls: [] }) diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/delete-keys-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transformation/delete-keys-config.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/rule-node/transform/delete-keys-config.component.html rename to ui-ngx/src/app/modules/home/components/rule-node/transformation/delete-keys-config.component.html diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/delete-keys-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/transformation/delete-keys-config.component.ts similarity index 100% rename from ui-ngx/src/app/modules/home/components/rule-node/transform/delete-keys-config.component.ts rename to ui-ngx/src/app/modules/home/components/rule-node/transformation/delete-keys-config.component.ts diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/node-json-path-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transformation/node-json-path-config.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/rule-node/transform/node-json-path-config.component.html rename to ui-ngx/src/app/modules/home/components/rule-node/transformation/node-json-path-config.component.html diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/node-json-path-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/transformation/node-json-path-config.component.ts similarity index 100% rename from ui-ngx/src/app/modules/home/components/rule-node/transform/node-json-path-config.component.ts rename to ui-ngx/src/app/modules/home/components/rule-node/transformation/node-json-path-config.component.ts diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/rename-keys-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transformation/rename-keys-config.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/rule-node/transform/rename-keys-config.component.html rename to ui-ngx/src/app/modules/home/components/rule-node/transformation/rename-keys-config.component.html diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/rename-keys-config.component.scss b/ui-ngx/src/app/modules/home/components/rule-node/transformation/rename-keys-config.component.scss similarity index 100% rename from ui-ngx/src/app/modules/home/components/rule-node/transform/rename-keys-config.component.scss rename to ui-ngx/src/app/modules/home/components/rule-node/transformation/rename-keys-config.component.scss diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/rename-keys-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/transformation/rename-keys-config.component.ts similarity index 100% rename from ui-ngx/src/app/modules/home/components/rule-node/transform/rename-keys-config.component.ts rename to ui-ngx/src/app/modules/home/components/rule-node/transformation/rename-keys-config.component.ts diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/script-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transformation/script-config.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/rule-node/transform/script-config.component.html rename to ui-ngx/src/app/modules/home/components/rule-node/transformation/script-config.component.html diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/script-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/transformation/script-config.component.ts similarity index 100% rename from ui-ngx/src/app/modules/home/components/rule-node/transform/script-config.component.ts rename to ui-ngx/src/app/modules/home/components/rule-node/transformation/script-config.component.ts diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/to-email-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transformation/to-email-config.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/rule-node/transform/to-email-config.component.html rename to ui-ngx/src/app/modules/home/components/rule-node/transformation/to-email-config.component.html diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/to-email-config.component.scss b/ui-ngx/src/app/modules/home/components/rule-node/transformation/to-email-config.component.scss similarity index 100% rename from ui-ngx/src/app/modules/home/components/rule-node/transform/to-email-config.component.scss rename to ui-ngx/src/app/modules/home/components/rule-node/transformation/to-email-config.component.scss diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/to-email-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/transformation/to-email-config.component.ts similarity index 100% rename from ui-ngx/src/app/modules/home/components/rule-node/transform/to-email-config.component.ts rename to ui-ngx/src/app/modules/home/components/rule-node/transformation/to-email-config.component.ts diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transform/rule-node-config-transform.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/transformation/transformation-rule-node-config.module.ts similarity index 88% rename from ui-ngx/src/app/modules/home/components/rule-node/transform/rule-node-config-transform.module.ts rename to ui-ngx/src/app/modules/home/components/rule-node/transformation/transformation-rule-node-config.module.ts index c64381e54e..a25495d7b4 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/transform/rule-node-config-transform.module.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/transformation/transformation-rule-node-config.module.ts @@ -18,7 +18,7 @@ import { NgModule, Type } from '@angular/core'; import { CommonModule } from '@angular/common'; import { IRuleNodeConfigurationComponent, SharedModule } from '@shared/public-api'; import { ChangeOriginatorConfigComponent } from './change-originator-config.component'; -import { RuleNodeConfigCommonModule } from '../common/rule-node-config-common.module'; +import { CommonRuleNodeConfigModule } from '../common/common-rule-node-config.module'; import { TransformScriptConfigComponent } from './script-config.component'; import { ToEmailConfigComponent } from './to-email-config.component'; import { CopyKeysConfigComponent } from './copy-keys-config.component'; @@ -42,7 +42,7 @@ import { ScriptConfigComponent } from '@home/components/rule-node/filter/script- imports: [ CommonModule, SharedModule, - RuleNodeConfigCommonModule + CommonRuleNodeConfigModule ], exports: [ ChangeOriginatorConfigComponent, @@ -55,13 +55,13 @@ import { ScriptConfigComponent } from '@home/components/rule-node/filter/script- DeduplicationConfigComponent ] }) -export class RuleNodeConfigTransformModule { +export class TransformationRuleNodeConfigModule { } -export const ruleNodeTransformConfigComponentsMap: Record> = { +export const transformationRuleNodeConfigComponentsMap: Record> = { 'tbTransformationNodeChangeOriginatorConfig': ChangeOriginatorConfigComponent, 'tbTransformationNodeCopyKeysConfig': CopyKeysConfigComponent, - 'tbActionNodeMsgDeduplicationConfig': DeduplicationConfigComponent, + 'tbTransformationNodeDeduplicationConfig': DeduplicationConfigComponent, 'tbTransformationNodeDeleteKeysConfig': DeleteKeysConfigComponent, 'tbTransformationNodeJsonPathConfig': NodeJsonPathConfigComponent, 'tbTransformationNodeRenameKeysConfig': RenameKeysConfigComponent, diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index bbcc04d5f9..cbae98683c 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -4927,7 +4927,7 @@ "min-interval-seconds-message": "Only 1 second minimum interval is allowed.", "output-timeseries-key-prefix": "Output time series key prefix", "output-timeseries-key-prefix-required": "Output time series key prefix required.", - "separator-hint": "Press \"Enter\" to complete field input.", + "separator-hint": "You should press \"Enter\" to complete field input.", "select-details": "Select details", "entity-details-id": "Id", "entity-details-title": "Title", From d961b89f31b974b800dee69a0018807dbe701ff2 Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 8 Jan 2025 16:17:14 +0200 Subject: [PATCH 045/108] tbel: add hexToBytesArray, base64ToBytesList --- .../service/script/TbelInvokeDocsIoTest.java | 24 ++++- .../thingsboard/script/api/tbel/TbUtils.java | 96 +++++++++++++++---- .../script/api/tbel/TbUtilsTest.java | 36 ++++++- 3 files changed, 134 insertions(+), 22 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeDocsIoTest.java b/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeDocsIoTest.java index cc51a7063c..91eae788c3 100644 --- a/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeDocsIoTest.java +++ b/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeDocsIoTest.java @@ -1467,6 +1467,26 @@ class TbelInvokeDocsIoTest extends AbstractTbelInvokeTest { assertEquals(expected, actual); } + // hexToBytes List or Array + @Test + public void hexToBytes_Test() throws ExecutionException, InterruptedException { + msgStr = "{}"; + decoderStr = """ + var validInputList = "0x01752B0367FA000500010488FFFFFFFFFFFFFFFF33"; + var validInputArray = "AABBCCDDEE"; + return { + "hexToBytes": hexToBytes(validInputList), + "hexToBytesArray": hexToBytesArray(validInputArray), + } + """; + Object actual = invokeScript(evalScript(decoderStr), msgStr); + LinkedHashMap expected = new LinkedHashMap<>(); + expected.put("hexToBytes", bytesToList(new byte[]{1, 117, 43, 3, 103, -6, 0, 5, 0, 1, 4, -120, -1, -1, -1, -1, -1, -1, -1, -1, 51})); + // [-86, -69, -52, -35, -18] == new byte[]{(byte) 0xAA, (byte) 0xBB, (byte) 0xCC, (byte) 0xDD, (byte) 0xEE} + expected.put("hexToBytesArray", bytesToList(new byte[]{(byte) 0xAA, (byte) 0xBB, (byte) 0xCC, (byte) 0xDD, (byte) 0xEE})); + assertEquals( expected, actual); + } + // parseBinaryArray @Test public void parseBinaryArray_Test() throws ExecutionException, InterruptedException { @@ -1759,13 +1779,15 @@ class TbelInvokeDocsIoTest extends AbstractTbelInvokeTest { return { "base64ToHex": base64ToHex("Kkk="), "bytesToBase64": bytesToBase64([42, 73]), - "base64ToBytes": base64ToBytes("Kkk=") + "base64ToBytes": base64ToBytes("Kkk="), + "base64ToBytesList": base64ToBytesList("AQIDBAU=") } """; LinkedHashMap expected = new LinkedHashMap<>(); expected.put("base64ToHex", "2A49"); expected.put("bytesToBase64", "Kkk="); expected.put("base64ToBytes", bytesToList(new byte[]{42, 73})); + expected.put("base64ToBytesList", bytesToList(new byte[]{1, 2, 3, 4, 5})); Object actual = invokeScript(evalScript(decoderStr), msgStr); assertEquals(expected, actual); } diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java index 6b321a3570..fae91d14ca 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java @@ -257,6 +257,8 @@ public class TbUtils { float.class, int.class))); parserConfig.addImport("hexToBytes", new MethodStub(TbUtils.class.getMethod("hexToBytes", ExecutionContext.class, String.class))); + parserConfig.addImport("hexToBytesArray", new MethodStub(TbUtils.class.getMethod("hexToBytesArray", + String.class))); parserConfig.addImport("intToHex", new MethodStub(TbUtils.class.getMethod("intToHex", Integer.class))); parserConfig.addImport("intToHex", new MethodStub(TbUtils.class.getMethod("intToHex", @@ -297,6 +299,8 @@ public class TbUtils { String.class))); parserConfig.addImport("base64ToBytes", new MethodStub(TbUtils.class.getMethod("base64ToBytes", String.class))); + parserConfig.addImport("base64ToBytesList", new MethodStub(TbUtils.class.getMethod("base64ToBytesList", + ExecutionContext.class, String.class))); parserConfig.addImport("bytesToBase64", new MethodStub(TbUtils.class.getMethod("bytesToBase64", byte[].class))); parserConfig.addImport("bytesToHex", new MethodStub(TbUtils.class.getMethod("bytesToHex", @@ -335,7 +339,7 @@ public class TbUtils { byte.class))); parserConfig.addImport("parseByteToBinaryArray", new MethodStub(TbUtils.class.getMethod("parseByteToBinaryArray", byte.class, int.class))); - parserConfig.addImport("parseByteToBinaryArray", new MethodStub(TbUtils.class.getMethod("parseByteToBinaryArray", + parserConfig.addImport("parseByteToBinaryArray", new MethodStub(TbUtils.class.getMethod("parseByteToBinaryArray", byte.class, int.class, boolean.class))); parserConfig.addImport("parseBytesToBinaryArray", new MethodStub(TbUtils.class.getMethod("parseBytesToBinaryArray", List.class))); @@ -664,23 +668,16 @@ public class TbUtils { } public static ExecutionArrayList hexToBytes(ExecutionContext ctx, String value) { - String hex = prepareNumberString(value, true); - if (hex == null) { - throw new IllegalArgumentException("Hex string must be not empty!"); - } - int len = hex.length(); - if (len % 2 > 0) { - throw new IllegalArgumentException("Hex string must be even-length."); - } - int radix = isHexadecimal(value); - if (radix != HEX_RADIX) { - throw new NumberFormatException("Value: \"" + value + "\" is not numeric or hexDecimal format!"); - } - - byte [] data = hexToBytes(hex); + String hex = validateAndPrepareHex(value); + byte[] data = hexToBytes(hex); return bytesToExecutionArrayList(ctx, data); } + public static byte[] hexToBytesArray(String value) { + String hex = validateAndPrepareHex(value); + return hexToBytes(hex); + } + public static List printUnsignedBytes(ExecutionContext ctx, List byteArray) { ExecutionArrayList data = new ExecutionArrayList<>(ctx); for (Byte b : byteArray) { @@ -839,6 +836,11 @@ public class TbUtils { return Base64.getDecoder().decode(input); } + public static ExecutionArrayList base64ToBytesList(ExecutionContext ctx, String input) { + byte[] bytes = Base64.getDecoder().decode(input); + return bytesToExecutionArrayList(ctx, bytes); + } + public static int parseBytesToInt(List data) { return parseBytesToInt(data, 0); } @@ -879,6 +881,48 @@ public class TbUtils { return bb.getInt(); } + public static long parseBytesToUnsignedInt(byte[] data) { + return parseBytesToUnsignedInt(data, 0); + } + + public static long parseBytesToUnsignedInt(byte[] data, int offset) { + return parseBytesToUnsignedInt(data, offset, validateLength(data.length, offset, BYTES_LEN_INT_MAX)); + } + + public static long parseBytesToUnsignedInt(byte[] data, int offset, int length) { + return parseBytesToUnsignedInt(data, offset, length, true); + } + + public static long parseBytesToUnsignedInt(byte[] data, int offset, int length, boolean bigEndian) { + validationNumberByLength(data, offset, length, BYTES_LEN_INT_MAX); + + ByteBuffer bb = ByteBuffer.allocate(8); + if (!bigEndian) { + bb.order(ByteOrder.LITTLE_ENDIAN); + } + bb.position(bigEndian ? 8 - length : 0); + bb.put(data, offset, length); + bb.position(0); + + return bb.getLong(); + } + + public static long parseBytesToUnsignedInt(List data) { + return parseBytesToUnsignedInt(data, 0); + } + + public static long parseBytesToUnsignedInt(List data, int offset) { + return parseBytesToUnsignedInt(data, offset, validateLength(data.size(), offset, BYTES_LEN_INT_MAX)); + } + + public static long parseBytesToUnsignedInt(List data, int offset, int length) { + return parseBytesToUnsignedInt(data, offset, length, true); + } + + public static long parseBytesToUnsignedInt(List data, int offset, int length, boolean bigEndian) { + return parseBytesToUnsignedInt(Bytes.toArray(data), offset, length, bigEndian); + } + public static long parseBytesToLong(List data) { return parseBytesToLong(data, 0); } @@ -1304,7 +1348,7 @@ public class TbUtils { public static byte[] parseByteToBinaryArray(byte byteValue, int binLength, boolean bigEndian) { byte[] bins = new byte[binLength]; for (int i = 0; i < binLength; i++) { - if(bigEndian) { + if (bigEndian) { bins[binLength - 1 - i] = (byte) ((byteValue >> i) & 1); } else { bins[i] = (byte) ((byteValue >> i) & 1); @@ -1435,16 +1479,32 @@ public class TbUtils { } private static byte[] hexToBytes(String hex) { - byte [] data = new byte[hex.length()/2]; + byte[] data = new byte[hex.length() / 2]; for (int i = 0; i < hex.length(); i += 2) { // Extract two characters from the hex string String byteString = hex.substring(i, i + 2); // Parse the hex string to a byte byte byteValue = (byte) Integer.parseInt(byteString, HEX_RADIX); // Add the byte to the ArrayList - data[i/2] = byteValue; + data[i / 2] = byteValue; } return data; } + + private static String validateAndPrepareHex(String value) { + String hex = prepareNumberString(value, true); + if (hex == null) { + throw new IllegalArgumentException("Hex string must be not empty!"); + } + int len = hex.length(); + if (len % 2 > 0) { + throw new IllegalArgumentException("Hex string must be even-length."); + } + int radix = isHexadecimal(value); + if (radix != HEX_RADIX) { + throw new NumberFormatException("Value: \"" + value + "\" is not numeric or hexDecimal format!"); + } + return hex; + } } diff --git a/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java index 1c5a1a2e1a..21ee8538a0 100644 --- a/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java +++ b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java @@ -19,7 +19,6 @@ import com.google.common.collect.Lists; import com.google.common.primitives.Bytes; import com.google.common.primitives.Ints; import lombok.extern.slf4j.Slf4j; -import org.checkerframework.checker.units.qual.A; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -36,6 +35,7 @@ import java.math.BigInteger; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; +import java.util.Base64; import java.util.Calendar; import java.util.Collections; import java.util.List; @@ -787,8 +787,12 @@ public class TbUtilsTest { public void hexToBytes_Test() { String input = "0x01752B0367FA000500010488FFFFFFFFFFFFFFFF33"; byte[] expected = {1, 117, 43, 3, 103, -6, 0, 5, 0, 1, 4, -120, -1, -1, -1, -1, -1, -1, -1, -1, 51}; - List actual = TbUtils.hexToBytes(ctx, input); - Assertions.assertEquals(toList(expected), actual); + List actualList = TbUtils.hexToBytes(ctx, input); + Assertions.assertEquals(toList(expected), actualList); + String validInput = "AABBCCDDEE"; + expected = new byte[]{(byte) 0xAA, (byte) 0xBB, (byte) 0xCC, (byte) 0xDD, (byte) 0xEE}; + byte[] actualBytes = TbUtils.hexToBytesArray(validInput); + Assertions.assertArrayEquals(expected, actualBytes); try { input = "0x01752B0367FA000500010488FFFFFFFFFFFFFFFF3"; TbUtils.hexToBytes(ctx, input); @@ -807,6 +811,12 @@ public class TbUtilsTest { } catch (IllegalArgumentException e) { Assertions.assertTrue(e.getMessage().contains("Hex string must be not empty")); } + try { + input = null; + TbUtils.hexToBytes(ctx, input); + } catch (IllegalArgumentException e) { + Assertions.assertTrue(e.getMessage().contains("Hex string must be not empty")); + } } @Test @@ -1086,6 +1096,26 @@ public class TbUtilsTest { String actual = TbUtils.hexToBase64(hex); Assertions.assertEquals(expected, actual); } + + @Test + void base64ToBytesList_Test() { + String validInput = Base64.getEncoder().encodeToString(new byte[]{1, 2, 3, 4, 5}); + ExecutionArrayList actual = TbUtils.base64ToBytesList(ctx, validInput); + ExecutionArrayList expected = new ExecutionArrayList<>(ctx); + expected.addAll(List.of((byte) 1, (byte)2, (byte)3, (byte)4, (byte)5)); + Assertions.assertEquals(expected, actual); + + String emptyInput = Base64.getEncoder().encodeToString(new byte[]{}); + actual = TbUtils.base64ToBytesList(ctx, emptyInput); + Assertions.assertTrue(actual.isEmpty()); + String invalidInput = "NotAValidBase64String"; + Assertions.assertThrows(IllegalArgumentException.class, () -> { + TbUtils.base64ToBytesList(ctx, invalidInput); + }); + Assertions.assertThrows(NullPointerException.class, () -> { + TbUtils.base64ToBytesList(ctx, null); + }); + } @Test public void bytesToHex_Test() { byte[] bb = {(byte) 0xBB, (byte) 0xAA}; From 107f7237a9647cc28c581250abe9f15009cbc482 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 8 Jan 2025 17:36:25 +0200 Subject: [PATCH 046/108] UI: Remove rule node unnecessary translate pipe --- .../common/device-relations-query-config.component.html | 2 +- .../rule-node/common/select-attributes.component.html | 2 +- .../get-telemetry-from-database-config.component.html | 2 +- .../transformation/deduplication-config.component.html | 6 +++--- .../rule-node/transformation/to-email-config.component.html | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/device-relations-query-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/device-relations-query-config.component.html index 56d8c948d9..902ce3efc4 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/device-relations-query-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/device-relations-query-config.component.html @@ -16,7 +16,7 @@ -->
    -
    +
    relation.direction diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/select-attributes.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/select-attributes.component.html index ab7bea5409..06d63535e9 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/select-attributes.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/select-attributes.component.html @@ -16,7 +16,7 @@ -->
    - -
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transformation/deduplication-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transformation/deduplication-config.component.html index e6309df7eb..5d5ec40580 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/transformation/deduplication-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/transformation/deduplication-config.component.html @@ -37,14 +37,14 @@ {{ deduplicationStrategiesTranslations.get(strategy) | translate }} - - - diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transformation/to-email-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transformation/to-email-config.component.html index f28a6d4737..87106d836d 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/transformation/to-email-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/transformation/to-email-config.component.html @@ -45,7 +45,7 @@
    tb.rulenode.recipients
    -
    @@ -82,7 +82,7 @@
    tb.rulenode.message-subject-and-content
    -
    From f57d46af3727908cf2cbba43457600920351b158 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 8 Jan 2025 17:36:38 +0200 Subject: [PATCH 047/108] UI: Remove rulenode-core-config.js --- .../resources/public/static/rulenode/rulenode-core-config.js | 1 - 1 file changed, 1 deletion(-) delete mode 100644 rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js diff --git a/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js b/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js deleted file mode 100644 index 2ad9ac666b..0000000000 --- a/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js +++ /dev/null @@ -1 +0,0 @@ -System.register(["@angular/core","@shared/public-api","@ngrx/store","@angular/forms","@core/public-api","@ngx-translate/core","@angular/cdk/keycodes","@angular/common","@home/components/public-api","tslib","rxjs","@angular/cdk/coercion","rxjs/operators"],(function(e){"use strict";var t,n,r,a,i,o,l,s,p,m,d,u,c,f,g,h,y,b,v,x,C,S,T,I,E,F,q,A,k,N,w,M,B,V,O,D,L,P,R,_,j,G,K,U,H,z,$,Q,J,Y,W,X,Z,ee,te,ne,re,ae,ie;return{setters:[function(e){t=e,n=e.EventEmitter,r=e.forwardRef,a=e.ɵNG_COMP_DEF},function(e){i=e.RuleNodeConfigurationComponent,o=e.AttributeScope,l=e.telemetryTypeTranslations,s=e.ScriptLanguage,p=e.AlarmSeverity,m=e.alarmSeverityTranslations,d=e.EntitySearchDirection,u=e.EntityType,c=e.entityFields,f=e.messageTypeNames,g=e.MessageType,h=e.coerceBoolean,y=e.PageComponent,b=e.entitySearchDirectionTranslations,v=e,x=e.AlarmStatus,C=e.alarmStatusTranslations,S=e.SharedModule,T=e.AggregationType,I=e.aggregationTranslations,E=e.NotificationType,F=e.SlackChanelType,q=e.SlackChanelTypesTranslateMap},function(e){A=e},function(e){k=e,N=e.Validators,w=e.FormArray,M=e.FormGroup,B=e.NgControl,V=e.NG_VALUE_ACCESSOR,O=e.NG_VALIDATORS},function(e){D=e.getCurrentAuthState,L=e,P=e.isDefinedAndNotNull,R=e.isEqual,_=e.deepTrim,j=e.isObject,G=e.isNotEmptyStr},function(e){K=e},function(e){U=e.ENTER,H=e.COMMA,z=e.SEMICOLON},function(e){$=e.CommonModule},function(e){Q=e.HomeComponentsModule},function(e){J=e.__decorate},function(e){Y=e.Subject,W=e.takeUntil,X=e.of},function(e){Z=e.coerceBooleanProperty},function(e){ee=e.startWith,te=e.map,ne=e.mergeMap,re=e.share,ae=e.tap,ie=e.takeUntil}],execute:function(){class oe extends i{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.emptyConfigForm}onConfigurationSet(e){this.emptyConfigForm=this.fb.group({})}static{this.ɵfac=function(e){return new(e||oe)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:oe,selectors:[["tb-node-empty-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:1,vars:0,template:function(e,n){1&e&&t.ɵɵelement(0,"div")},dependencies:t.ɵɵgetComponentDepsFactory(oe),encapsulation:2})}}function le(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.customer-name-pattern-required")," "))}e("EmptyConfigComponent",oe);class se extends i{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.assignCustomerConfigForm}onConfigurationSet(e){this.assignCustomerConfigForm=this.fb.group({customerNamePattern:[e?e.customerNamePattern:null,[N.required,N.pattern(/.*\S.*/)]],createCustomerIfNotExists:[!!e&&e.createCustomerIfNotExists,[]]})}prepareOutputConfig(e){return e.customerNamePattern=e.customerNamePattern.trim(),e}static{this.ɵfac=function(e){return new(e||se)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:se,selectors:[["tb-action-node-assign-to-customer-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:13,vars:5,consts:[[1,"flex","flex-col",3,"formGroup"],[1,"tb-form-panel","no-padding","no-border"],["subscriptSizing","dynamic",1,"mat-block"],["translate",""],["required","","matInput","","formControlName","customerNamePattern"],[4,"ngIf"],[1,"tb-form-row"],["formControlName","createCustomerIfNotExists",1,"mat-slide"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1)(2,"mat-form-field",2)(3,"mat-label",3),t.ɵɵtext(4,"tb.rulenode.customer-name-pattern"),t.ɵɵelementEnd(),t.ɵɵelement(5,"input",4),t.ɵɵtemplate(6,le,3,3,"mat-error",5),t.ɵɵelementStart(7,"mat-hint",3),t.ɵɵtext(8,"tb.rulenode.customer-name-pattern-hint"),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"div",6)(10,"mat-slide-toggle",7),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd()()()()),2&e&&(t.ɵɵproperty("formGroup",n.assignCustomerConfigForm),t.ɵɵadvance(6),t.ɵɵproperty("ngIf",n.assignCustomerConfigForm.get("customerNamePattern").hasError("required")||n.assignCustomerConfigForm.get("customerNamePattern").hasError("pattern")),t.ɵɵadvance(5),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(12,3,"tb.rulenode.create-customer-if-not-exists")," "))},dependencies:t.ɵɵgetComponentDepsFactory(se),encapsulation:2})}}e("AssignCustomerConfigComponent",se);const pe=()=>({standalone:!0});function me(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",15),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.telemetryTypeTranslationsMap.get(e))," ")}}function de(e,n){1&e&&(t.ɵɵelementStart(0,"div",12),t.ɵɵpipe(1,"translate"),t.ɵɵelementStart(2,"mat-slide-toggle",16),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(1,2,"tb.rulenode.send-attributes-updated-notification-hint")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,4,"tb.rulenode.send-attributes-updated-notification")," "))}function ue(e,n){1&e&&(t.ɵɵelementStart(0,"div",12),t.ɵɵpipe(1,"translate"),t.ɵɵelementStart(2,"mat-slide-toggle",17),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(1,2,"tb.rulenode.notify-device-on-update-hint")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,4,"tb.rulenode.notify-device")," "))}class ce extends i{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopeMap=o,this.attributeScopes=Object.keys(o),this.telemetryTypeTranslationsMap=l}configForm(){return this.attributesConfigForm}onConfigurationSet(e){this.attributesConfigForm=this.fb.group({scope:[e?e.scope:null,[N.required]],notifyDevice:[!e||e.notifyDevice,[]],sendAttributesUpdatedNotification:[!!e&&e.sendAttributesUpdatedNotification,[]],updateAttributesOnlyOnValueChange:[!!e&&e.updateAttributesOnlyOnValueChange,[]]}),this.attributesConfigForm.get("scope").valueChanges.subscribe((e=>{e!==o.SHARED_SCOPE&&this.attributesConfigForm.get("notifyDevice").patchValue(!1,{emitEvent:!1}),e===o.CLIENT_SCOPE&&this.attributesConfigForm.get("sendAttributesUpdatedNotification").patchValue(!1,{emitEvent:!1}),this.attributesConfigForm.get("updateAttributesOnlyOnValueChange").patchValue(!1,{emitEvent:!1})}))}static{this.ɵfac=function(e){return new(e||ce)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:ce,selectors:[["tb-action-node-attributes-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:31,vars:24,consts:[[1,"tb-form-panel","no-border","no-padding",3,"formGroup"],[1,"tb-form-panel","stroked"],[3,"hintText"],[1,"tb-form-row","no-border","no-padding","tb-standard-fields"],[1,"flex"],["required","","matInput","","formControlName","scope",1,"tb-entity-type-select"],[3,"value",4,"ngFor","ngForOf"],["type","text","matInput","","readonly","","disabled","",3,"ngModel","ngModelOptions"],["type","button","matSuffix","","mat-icon-button","","aria-label","Copy","ngxClipboard","",3,"cbContent","matTooltip"],["aria-hidden","false","aria-label","help-icon"],[1,"tb-settings"],["translate",""],[1,"tb-form-row","no-border","no-padding",3,"tb-hint-tooltip-icon"],["formControlName","updateAttributesOnlyOnValueChange",1,"mat-slide"],["class","tb-form-row no-border no-padding",3,"tb-hint-tooltip-icon",4,"ngIf"],[3,"value"],["formControlName","sendAttributesUpdatedNotification",1,"mat-slide"],["formControlName","notifyDevice",1,"mat-slide"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1),t.ɵɵelement(2,"tb-example-hint",2),t.ɵɵelementStart(3,"div",3)(4,"mat-form-field",4)(5,"mat-label"),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(8,"mat-select",5),t.ɵɵtemplate(9,me,3,4,"mat-option",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"mat-form-field",4)(11,"mat-label"),t.ɵɵtext(12),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(14,"input",7),t.ɵɵelementStart(15,"button",8),t.ɵɵpipe(16,"translate"),t.ɵɵelementStart(17,"mat-icon",9),t.ɵɵtext(18,"content_copy "),t.ɵɵelementEnd()()()()(),t.ɵɵelementStart(19,"section",1)(20,"mat-expansion-panel",10)(21,"mat-expansion-panel-header")(22,"mat-panel-title",11),t.ɵɵtext(23,"tb.rulenode.advanced-settings"),t.ɵɵelementEnd()(),t.ɵɵelementStart(24,"div",12),t.ɵɵpipe(25,"translate"),t.ɵɵelementStart(26,"mat-slide-toggle",13),t.ɵɵtext(27),t.ɵɵpipe(28,"translate"),t.ɵɵelementEnd()(),t.ɵɵtemplate(29,de,5,6,"div",14)(30,ue,5,6,"div",14),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.attributesConfigForm),t.ɵɵadvance(2),t.ɵɵproperty("hintText","tb.rulenode.attributes-scope-hint"),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(7,13,"tb.rulenode.attributes-scope")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.attributeScopes),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(13,15,"tb.rulenode.attributes-scope-value")),t.ɵɵadvance(2),t.ɵɵproperty("ngModel",n.attributesConfigForm.get("scope").value)("ngModelOptions",t.ɵɵpureFunction0(23,pe)),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(16,17,"tb.rulenode.attributes-scope-value-copy")),t.ɵɵproperty("cbContent",n.attributesConfigForm.get("scope").value),t.ɵɵadvance(9),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(25,19,n.attributesConfigForm.get("updateAttributesOnlyOnValueChange").value?"tb.rulenode.update-attributes-only-on-value-change-hint-enabled":"tb.rulenode.update-attributes-only-on-value-change-hint")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(28,21,"tb.rulenode.update-attributes-only-on-value-change")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.attributesConfigForm.get("scope").value!==n.attributeScopeMap.CLIENT_SCOPE),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.attributesConfigForm.get("scope").value===n.attributeScopeMap.SHARED_SCOPE))},dependencies:t.ɵɵgetComponentDepsFactory(ce),encapsulation:2})}}e("AttributesConfigComponent",ce);const fe=["jsFuncComponent"],ge=["tbelFuncComponent"],he=()=>["msg","metadata","msgType"];function ye(e,n){1&e&&t.ɵɵelement(0,"tb-script-lang",12)}function be(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-js-func",13,0)(2,"button",14),t.ɵɵpipe(3,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.testScript())})),t.ɵɵelementStart(4,"mat-icon",15),t.ɵɵtext(5,"bug_report"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("functionArgs",t.ɵɵpureFunction0(4,he)),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(3,2,e.testScriptLabel))}}function ve(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-js-func",16,1)(2,"button",14),t.ɵɵpipe(3,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.testScript())})),t.ɵɵelementStart(4,"mat-icon",15),t.ɵɵtext(5,"bug_report"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("functionArgs",t.ɵɵpureFunction0(6,he))("disableUndefinedCheck",!0)("scriptLanguage",e.scriptLanguage.TBEL),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(3,4,e.testScriptLabel))}}function xe(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.alarm-type-required")," "))}class Ce extends i{constructor(e,t,r,a){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=r,this.translate=a,this.tbelEnabled=D(this.store).tbelEnabled,this.scriptLanguage=s,this.changeScript=new n,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-details-function"}configForm(){return this.clearAlarmConfigForm}onConfigurationSet(e){this.clearAlarmConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:s.JS,[N.required]],alarmDetailsBuildJs:[e?e.alarmDetailsBuildJs:null,[]],alarmDetailsBuildTbel:[e?e.alarmDetailsBuildTbel:null,[]],alarmType:[e?e.alarmType:null,[N.required]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.clearAlarmConfigForm.get("scriptLang").value;t!==s.TBEL||this.tbelEnabled||(t=s.JS,this.clearAlarmConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.clearAlarmConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.clearAlarmConfigForm.get("alarmDetailsBuildJs").setValidators(t===s.JS?[N.required]:[]),this.clearAlarmConfigForm.get("alarmDetailsBuildJs").updateValueAndValidity({emitEvent:e}),this.clearAlarmConfigForm.get("alarmDetailsBuildTbel").setValidators(t===s.TBEL?[N.required]:[]),this.clearAlarmConfigForm.get("alarmDetailsBuildTbel").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=s.JS)),e}testScript(e){const t=this.clearAlarmConfigForm.get("scriptLang").value,n=t===s.JS?"alarmDetailsBuildJs":"alarmDetailsBuildTbel",r=t===s.JS?"rulenode/clear_alarm_node_script_fn":"rulenode/tbel/clear_alarm_node_script_fn",a=this.clearAlarmConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(a,"json",this.translate.instant("tb.rulenode.details"),"Details",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.clearAlarmConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.clearAlarmConfigForm.get("scriptLang").value===s.JS&&this.jsFuncComponent.validateOnSubmit()}static{this.ɵfac=function(e){return new(e||Ce)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder),t.ɵɵdirectiveInject(L.NodeScriptTestService),t.ɵɵdirectiveInject(K.TranslateService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Ce,selectors:[["tb-action-node-clear-alarm-config"]],viewQuery:function(e,n){if(1&e&&(t.ɵɵviewQuery(fe,5),t.ɵɵviewQuery(ge,5)),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.jsFuncComponent=e.first),t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.tbelFuncComponent=e.first)}},features:[t.ɵɵInheritDefinitionFeature],decls:15,vars:8,consts:[["jsFuncComponent",""],["tbelFuncComponent",""],[1,"flex","flex-col",3,"formGroup"],["formControlName","scriptLang",4,"ngIf"],["formControlName","alarmDetailsBuildJs","functionName","Details","helpId","rulenode/clear_alarm_node_script_fn","noValidate","true",3,"functionArgs",4,"ngIf"],["formControlName","alarmDetailsBuildTbel","functionName","Details","helpId","rulenode/tbel/clear_alarm_node_script_fn","noValidate","true",3,"functionArgs","disableUndefinedCheck","scriptLanguage",4,"ngIf"],[1,"flex","flex-row",2,"padding-bottom","16px"],["mat-button","","mat-raised-button","","color","primary",3,"click"],["subscriptSizing","dynamic",1,"mat-block"],["translate",""],["required","","matInput","","formControlName","alarmType"],[4,"ngIf"],["formControlName","scriptLang"],["formControlName","alarmDetailsBuildJs","functionName","Details","helpId","rulenode/clear_alarm_node_script_fn","noValidate","true",3,"functionArgs"],["toolbarSuffixButton","","mat-icon-button","","matTooltipPosition","above",1,"tb-mat-32",3,"click","matTooltip"],["color","primary",1,"material-icons"],["formControlName","alarmDetailsBuildTbel","functionName","Details","helpId","rulenode/tbel/clear_alarm_node_script_fn","noValidate","true",3,"functionArgs","disableUndefinedCheck","scriptLanguage"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",2),t.ɵɵtemplate(1,ye,1,0,"tb-script-lang",3)(2,be,6,5,"tb-js-func",4)(3,ve,6,7,"tb-js-func",5),t.ɵɵelementStart(4,"div",6)(5,"button",7),t.ɵɵlistener("click",(function(){return n.testScript()})),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(8,"mat-form-field",8)(9,"mat-label",9),t.ɵɵtext(10,"tb.rulenode.alarm-type"),t.ɵɵelementEnd(),t.ɵɵelement(11,"input",10),t.ɵɵtemplate(12,xe,3,3,"mat-error",11),t.ɵɵelementStart(13,"mat-hint",9),t.ɵɵtext(14,"tb.rulenode.general-pattern-hint"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.clearAlarmConfigForm),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.tbelEnabled),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.clearAlarmConfigForm.get("scriptLang").value===n.scriptLanguage.JS),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.clearAlarmConfigForm.get("scriptLang").value===n.scriptLanguage.TBEL),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(7,6,n.testScriptLabel)," "),t.ɵɵadvance(6),t.ɵɵproperty("ngIf",n.clearAlarmConfigForm.get("alarmType").hasError("required")))},dependencies:t.ɵɵgetComponentDepsFactory(Ce),encapsulation:2})}}e("ClearAlarmConfigComponent",Ce);const Se=["jsFuncComponent"],Te=["tbelFuncComponent"],Ie=()=>["msg","metadata","msgType"];function Ee(e,n){1&e&&(t.ɵɵelementStart(0,"mat-checkbox",7),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.overwrite-alarm-details")," "))}function Fe(e,n){1&e&&t.ɵɵelement(0,"tb-script-lang",14)}function qe(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-js-func",15,0)(2,"button",16),t.ɵɵpipe(3,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(2);return t.ɵɵresetView(n.testScript())})),t.ɵɵelementStart(4,"mat-icon",17),t.ɵɵtext(5,"bug_report"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("functionArgs",t.ɵɵpureFunction0(4,Ie)),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(3,2,e.testScriptLabel))}}function Ae(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-js-func",18,1)(2,"button",16),t.ɵɵpipe(3,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext(2);return t.ɵɵresetView(n.testScript())})),t.ɵɵelementStart(4,"mat-icon",17),t.ɵɵtext(5,"bug_report"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("functionArgs",t.ɵɵpureFunction0(6,Ie))("disableUndefinedCheck",!0)("scriptLanguage",e.scriptLanguage.TBEL),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(3,4,e.testScriptLabel))}}function ke(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"section",8),t.ɵɵtemplate(1,Fe,1,0,"tb-script-lang",9)(2,qe,6,5,"tb-js-func",10)(3,Ae,6,7,"tb-js-func",11),t.ɵɵelementStart(4,"div",12)(5,"button",13),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.testScript())})),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.tbelEnabled),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.createAlarmConfigForm.get("scriptLang").value===e.scriptLanguage.JS),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.createAlarmConfigForm.get("scriptLang").value===e.scriptLanguage.TBEL),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(7,4,e.testScriptLabel)," ")}}function Ne(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.alarm-type-required")," "))}function we(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",32),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext(3);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.alarmSeverityTranslationMap.get(e))," ")}}function Me(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.alarm-severity-required")," "))}function Be(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",29)(1,"mat-label",20),t.ɵɵtext(2,"tb.rulenode.alarm-severity"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-select",30),t.ɵɵtemplate(4,we,3,4,"mat-option",31),t.ɵɵelementEnd(),t.ɵɵtemplate(5,Me,3,3,"mat-error",22),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(4),t.ɵɵproperty("ngForOf",e.alarmSeverities),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.createAlarmConfigForm.get("severity").hasError("required"))}}function Ve(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.alarm-severity-required")," "))}function Oe(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",19)(1,"mat-label",20),t.ɵɵtext(2,"tb.rulenode.alarm-severity-pattern"),t.ɵɵelementEnd(),t.ɵɵelement(3,"input",33),t.ɵɵtemplate(4,Ve,3,3,"mat-error",22),t.ɵɵelement(5,"mat-hint",34),t.ɵɵpipe(6,"translate"),t.ɵɵpipe(7,"safe"),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(4),t.ɵɵproperty("ngIf",e.createAlarmConfigForm.get("severity").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("innerHTML",t.ɵɵpipeBind2(7,4,t.ɵɵpipeBind1(6,2,"tb.rulenode.alarm-severity-pattern-hint"),"html"),t.ɵɵsanitizeHtml)}}function De(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-chip-row",38),t.ɵɵlistener("removed",(function(){const n=t.ɵɵrestoreView(e).$implicit,r=t.ɵɵnextContext(3);return t.ɵɵresetView(r.removeKey(n,"relationTypes"))})),t.ɵɵtext(1),t.ɵɵelementStart(2,"mat-icon",39),t.ɵɵtext(3,"close"),t.ɵɵelementEnd()()}if(2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function Le(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"section")(1,"mat-form-field",35)(2,"mat-label",20),t.ɵɵtext(3,"tb.rulenode.relation-types-list"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-chip-grid",null,2),t.ɵɵtemplate(6,De,4,1,"mat-chip-row",36),t.ɵɵelementStart(7,"input",37),t.ɵɵpipe(8,"translate"),t.ɵɵlistener("matChipInputTokenEnd",(function(n){t.ɵɵrestoreView(e);const r=t.ɵɵnextContext(2);return t.ɵɵresetView(r.addKey(n,"relationTypes"))})),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"mat-hint",20),t.ɵɵtext(10,"tb.rulenode.relation-types-list-hint"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵreference(5),n=t.ɵɵnextContext(2);t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",n.createAlarmConfigForm.get("relationTypes").value),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(8,5,"tb.rulenode.relation-types-list")),t.ɵɵproperty("matChipInputFor",e)("matChipInputSeparatorKeyCodes",n.separatorKeysCodes)("matChipInputAddOnBlur",!0)}}function Pe(e,n){if(1&e&&(t.ɵɵelementStart(0,"section",8)(1,"mat-form-field",19)(2,"mat-label",20),t.ɵɵtext(3,"tb.rulenode.alarm-type"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",21),t.ɵɵtemplate(5,Ne,3,3,"mat-error",22),t.ɵɵelementStart(6,"mat-hint",20),t.ɵɵtext(7,"tb.rulenode.general-pattern-hint"),t.ɵɵelementEnd()(),t.ɵɵelementStart(8,"mat-checkbox",23),t.ɵɵtext(9),t.ɵɵpipe(10,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(11,Be,6,2,"mat-form-field",24)(12,Oe,8,7,"mat-form-field",25),t.ɵɵelementStart(13,"mat-checkbox",26),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(16,Le,11,7,"section",22),t.ɵɵelementStart(17,"mat-checkbox",27),t.ɵɵtext(18),t.ɵɵpipe(19,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(20,"mat-checkbox",28),t.ɵɵtext(21),t.ɵɵpipe(22,"translate"),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(5),t.ɵɵproperty("ngIf",e.createAlarmConfigForm.get("alarmType").hasError("required")),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(10,8,"tb.rulenode.use-alarm-severity-pattern")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",!e.createAlarmConfigForm.get("dynamicSeverity").value),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.createAlarmConfigForm.get("dynamicSeverity").value),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(15,10,"tb.rulenode.propagate")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",!0===e.createAlarmConfigForm.get("propagate").value),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(19,12,"tb.rulenode.propagate-to-owner")," "),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(22,14,"tb.rulenode.propagate-to-tenant")," ")}}class Re extends i{constructor(e,t,r,a){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=r,this.translate=a,this.alarmSeverities=Object.keys(p),this.alarmSeverityTranslationMap=m,this.separatorKeysCodes=[U,H,z],this.tbelEnabled=D(this.store).tbelEnabled,this.scriptLanguage=s,this.changeScript=new n,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-details-function"}configForm(){return this.createAlarmConfigForm}onConfigurationSet(e){this.createAlarmConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:s.JS,[N.required]],alarmDetailsBuildJs:[e?e.alarmDetailsBuildJs:null,[]],alarmDetailsBuildTbel:[e?e.alarmDetailsBuildTbel:null,[]],useMessageAlarmData:[!!e&&e.useMessageAlarmData,[]],overwriteAlarmDetails:[!!e&&e.overwriteAlarmDetails,[]],alarmType:[e?e.alarmType:null,[]],severity:[e?e.severity:null,[]],propagate:[!!e&&e.propagate,[]],relationTypes:[e?e.relationTypes:null,[]],propagateToOwner:[!!e&&e.propagateToOwner,[]],propagateToTenant:[!!e&&e.propagateToTenant,[]],dynamicSeverity:!1}),this.createAlarmConfigForm.get("dynamicSeverity").valueChanges.subscribe((e=>{e?this.createAlarmConfigForm.get("severity").patchValue("",{emitEvent:!1}):this.createAlarmConfigForm.get("severity").patchValue(this.alarmSeverities[0],{emitEvent:!1})}))}validatorTriggers(){return["useMessageAlarmData","overwriteAlarmDetails","scriptLang"]}updateValidators(e){const t=this.createAlarmConfigForm.get("useMessageAlarmData").value,n=this.createAlarmConfigForm.get("overwriteAlarmDetails").value;t?(this.createAlarmConfigForm.get("alarmType").setValidators([]),this.createAlarmConfigForm.get("severity").setValidators([])):(this.createAlarmConfigForm.get("alarmType").setValidators([N.required]),this.createAlarmConfigForm.get("severity").setValidators([N.required])),this.createAlarmConfigForm.get("alarmType").updateValueAndValidity({emitEvent:e}),this.createAlarmConfigForm.get("severity").updateValueAndValidity({emitEvent:e});let r=this.createAlarmConfigForm.get("scriptLang").value;r!==s.TBEL||this.tbelEnabled||(r=s.JS,this.createAlarmConfigForm.get("scriptLang").patchValue(r,{emitEvent:!1}),setTimeout((()=>{this.createAlarmConfigForm.updateValueAndValidity({emitEvent:!0})})));const a=!1===t||!0===n;this.createAlarmConfigForm.get("alarmDetailsBuildJs").setValidators(a&&r===s.JS?[N.required]:[]),this.createAlarmConfigForm.get("alarmDetailsBuildTbel").setValidators(a&&r===s.TBEL?[N.required]:[]),this.createAlarmConfigForm.get("alarmDetailsBuildJs").updateValueAndValidity({emitEvent:e}),this.createAlarmConfigForm.get("alarmDetailsBuildTbel").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=s.JS)),e}testScript(e){const t=this.createAlarmConfigForm.get("scriptLang").value,n=t===s.JS?"alarmDetailsBuildJs":"alarmDetailsBuildTbel",r=t===s.JS?"rulenode/create_alarm_node_script_fn":"rulenode/tbel/create_alarm_node_script_fn",a=this.createAlarmConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(a,"json",this.translate.instant("tb.rulenode.details"),"Details",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.createAlarmConfigForm.get(n).setValue(e),this.changeScript.emit())}))}removeKey(e,t){const n=this.createAlarmConfigForm.get(t).value,r=n.indexOf(e);r>=0&&(n.splice(r,1),this.createAlarmConfigForm.get(t).setValue(n,{emitEvent:!0}))}addKey(e,t){const n=e.input;let r=e.value;if((r||"").trim()){r=r.trim();let e=this.createAlarmConfigForm.get(t).value;e&&-1!==e.indexOf(r)||(e||(e=[]),e.push(r),this.createAlarmConfigForm.get(t).setValue(e,{emitEvent:!0}))}n&&(n.value="")}onValidate(){const e=this.createAlarmConfigForm.get("useMessageAlarmData").value,t=this.createAlarmConfigForm.get("overwriteAlarmDetails").value;if(!e||t){this.createAlarmConfigForm.get("scriptLang").value===s.JS&&this.jsFuncComponent.validateOnSubmit()}}static{this.ɵfac=function(e){return new(e||Re)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder),t.ɵɵdirectiveInject(L.NodeScriptTestService),t.ɵɵdirectiveInject(K.TranslateService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Re,selectors:[["tb-action-node-create-alarm-config"]],viewQuery:function(e,n){if(1&e&&(t.ɵɵviewQuery(Se,5),t.ɵɵviewQuery(Te,5)),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.jsFuncComponent=e.first),t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.tbelFuncComponent=e.first)}},features:[t.ɵɵInheritDefinitionFeature],decls:7,vars:7,consts:[["jsFuncComponent",""],["tbelFuncComponent",""],["relationTypesChipList",""],[1,"flex","flex-col",3,"formGroup"],["formControlName","useMessageAlarmData"],["formControlName","overwriteAlarmDetails",4,"ngIf"],["class","flex flex-col",4,"ngIf"],["formControlName","overwriteAlarmDetails"],[1,"flex","flex-col"],["formControlName","scriptLang",4,"ngIf"],["formControlName","alarmDetailsBuildJs","functionName","Details","helpId","rulenode/create_alarm_node_script_fn","noValidate","true",3,"functionArgs",4,"ngIf"],["formControlName","alarmDetailsBuildTbel","functionName","Details","helpId","rulenode/tbel/create_alarm_node_script_fn","noValidate","true",3,"functionArgs","disableUndefinedCheck","scriptLanguage",4,"ngIf"],[1,"flex","flex-row",2,"padding-bottom","16px"],["mat-button","","mat-raised-button","","color","primary",3,"click"],["formControlName","scriptLang"],["formControlName","alarmDetailsBuildJs","functionName","Details","helpId","rulenode/create_alarm_node_script_fn","noValidate","true",3,"functionArgs"],["toolbarSuffixButton","","mat-icon-button","","matTooltipPosition","above",1,"tb-mat-32",3,"click","matTooltip"],["color","primary",1,"material-icons"],["formControlName","alarmDetailsBuildTbel","functionName","Details","helpId","rulenode/tbel/create_alarm_node_script_fn","noValidate","true",3,"functionArgs","disableUndefinedCheck","scriptLanguage"],["subscriptSizing","dynamic",1,"flex-1"],["translate",""],["required","","matInput","","formControlName","alarmType"],[4,"ngIf"],["formControlName","dynamicSeverity"],["class","flex-1",4,"ngIf"],["class","flex-1","subscriptSizing","dynamic",4,"ngIf"],["formControlName","propagate"],["formControlName","propagateToOwner"],["formControlName","propagateToTenant"],[1,"flex-1"],["formControlName","severity","required",""],[3,"value",4,"ngFor","ngForOf"],[3,"value"],["matInput","","formControlName","severity","required",""],[3,"innerHTML"],["floatLabel","always","subscriptSizing","dynamic",1,"mat-block"],[3,"removed",4,"ngFor","ngForOf"],["matInput","","type","text",3,"matChipInputTokenEnd","placeholder","matChipInputFor","matChipInputSeparatorKeyCodes","matChipInputAddOnBlur"],[3,"removed"],["matChipRemove",""]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",3)(1,"mat-checkbox",4),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(4,Ee,3,3,"mat-checkbox",5)(5,ke,8,6,"section",6)(6,Pe,23,16,"section",6),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.createAlarmConfigForm),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,5,"tb.rulenode.use-message-alarm-data")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",!0===n.createAlarmConfigForm.get("useMessageAlarmData").value),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!1===n.createAlarmConfigForm.get("useMessageAlarmData").value||!0===n.createAlarmConfigForm.get("overwriteAlarmDetails").value),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!1===n.createAlarmConfigForm.get("useMessageAlarmData").value))},dependencies:t.ɵɵgetComponentDepsFactory(Re),encapsulation:2})}}function _e(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",21),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.directionTypeTranslations.get(e))," ")}}function je(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",22)(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",23),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,e.entityTypeNamePatternTranslation.get(e.createRelationConfigForm.get("entityType").value)))}}function Ge(e,n){1&e&&(t.ɵɵelementStart(0,"mat-form-field",22)(1,"mat-label",5),t.ɵɵtext(2,"tb.rulenode.profile-name"),t.ɵɵelementEnd(),t.ɵɵelement(3,"input",24),t.ɵɵelementEnd())}function Ke(e,n){1&e&&t.ɵɵelement(0,"tb-example-hint",25),2&e&&t.ɵɵproperty("hintText","tb.rulenode.kv-map-pattern-hint")}function Ue(e,n){1&e&&(t.ɵɵelementStart(0,"div",26),t.ɵɵpipe(1,"translate"),t.ɵɵelementStart(2,"mat-slide-toggle",27),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(1,2,"tb.rulenode.create-entity-if-not-exists-hint")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,4,"tb.rulenode.create-entity-if-not-exists")," "))}e("CreateAlarmConfigComponent",Re);class He extends i{constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(d),this.directionTypeTranslations=new Map([[d.FROM,"tb.rulenode.search-direction-from"],[d.TO,"tb.rulenode.search-direction-to"]]),this.entityType=u,this.entityTypeNamePatternTranslation=new Map([[u.DEVICE,"tb.rulenode.device-name-pattern"],[u.ASSET,"tb.rulenode.asset-name-pattern"],[u.ENTITY_VIEW,"tb.rulenode.entity-view-name-pattern"],[u.CUSTOMER,"tb.rulenode.customer-title-pattern"],[u.USER,"tb.rulenode.user-name-pattern"],[u.DASHBOARD,"tb.rulenode.dashboard-name-pattern"],[u.EDGE,"tb.rulenode.edge-name-pattern"]]),this.allowedEntityTypes=[u.DEVICE,u.ASSET,u.ENTITY_VIEW,u.TENANT,u.CUSTOMER,u.USER,u.DASHBOARD,u.EDGE]}configForm(){return this.createRelationConfigForm}onConfigurationSet(e){this.createRelationConfigForm=this.fb.group({direction:[e?e.direction:null,[N.required]],entityType:[e?e.entityType:null,[N.required]],entityNamePattern:[e?e.entityNamePattern:null,[]],entityTypePattern:[e?e.entityTypePattern:null,[]],relationType:[e?e.relationType:null,[N.required]],createEntityIfNotExists:[!!e&&e.createEntityIfNotExists,[]],removeCurrentRelations:[!!e&&e.removeCurrentRelations,[]],changeOriginatorToRelatedEntity:[!!e&&e.changeOriginatorToRelatedEntity,[]]})}validatorTriggers(){return["entityType","createEntityIfNotExists"]}updateValidators(e){const t=this.createRelationConfigForm.get("entityType").value;if(t?this.createRelationConfigForm.get("entityNamePattern").setValidators([N.required,N.pattern(/.*\S.*/)]):this.createRelationConfigForm.get("entityNamePattern").setValidators([]),!t||t!==u.DEVICE&&t!==u.ASSET)this.createRelationConfigForm.get("entityTypePattern").setValidators([]);else{const e=[N.pattern(/.*\S.*/)];this.createRelationConfigForm.get("createEntityIfNotExists").value&&e.push(N.required),this.createRelationConfigForm.get("entityTypePattern").setValidators(e)}this.createRelationConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e}),this.createRelationConfigForm.get("entityTypePattern").updateValueAndValidity({emitEvent:e})}prepareOutputConfig(e){return e.entityNamePattern=e.entityNamePattern?e.entityNamePattern.trim():null,e.entityTypePattern=e.entityTypePattern?e.entityTypePattern.trim():null,e}static{this.ɵfac=function(e){return new(e||He)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:He,selectors:[["tb-action-node-create-relation-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:36,vars:19,consts:[[1,"tb-form-panel","no-padding","no-border",3,"formGroup"],[1,"tb-form-panel","stroked","no-padding-bottom"],["translate","",1,"tb-form-panel-title"],[1,"flex","flex-col"],["hideRequiredMarker","",1,"mat-block"],["translate",""],["required","","matInput","","formControlName","direction"],[3,"value",4,"ngFor","ngForOf"],["required","","formControlName","relationType"],[1,"flex","flex-row","gap-4"],["showLabel","","required","","formControlName","entityType",1,"flex-1",3,"allowedEntityTypes"],["class","mat-block flex-1",4,"ngIf"],[3,"hintText",4,"ngIf"],["style","margin-bottom: 18px","class","tb-form-row no-border no-padding",3,"tb-hint-tooltip-icon",4,"ngIf"],[1,"tb-form-panel","stroked","no-padding"],[1,"tb-settings"],[2,"padding","16px"],[1,"tb-form-panel","no-border","no-padding-top"],[1,"tb-form-row","no-border","no-padding",3,"tb-hint-tooltip-icon"],["formControlName","removeCurrentRelations",1,"mat-slide"],["formControlName","changeOriginatorToRelatedEntity",1,"mat-slide"],[3,"value"],[1,"mat-block","flex-1"],["required","","matInput","","formControlName","entityNamePattern"],["matInput","","formControlName","entityTypePattern"],[3,"hintText"],[1,"tb-form-row","no-border","no-padding",2,"margin-bottom","18px",3,"tb-hint-tooltip-icon"],["formControlName","createEntityIfNotExists",1,"mat-slide"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3,"tb.rulenode.relation-parameters"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",3)(5,"mat-form-field",4)(6,"mat-label",5),t.ɵɵtext(7,"relation.direction"),t.ɵɵelementEnd(),t.ɵɵelementStart(8,"mat-select",6),t.ɵɵtemplate(9,_e,3,4,"mat-option",7),t.ɵɵelementEnd()(),t.ɵɵelement(10,"tb-relation-type-autocomplete",8),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"div",1)(12,"div",2),t.ɵɵtext(13,"tb.rulenode.target-entity"),t.ɵɵelementEnd(),t.ɵɵelementStart(14,"div",9),t.ɵɵelement(15,"tb-entity-type-select",10),t.ɵɵtemplate(16,je,5,3,"mat-form-field",11)(17,Ge,4,0,"mat-form-field",11),t.ɵɵelementEnd(),t.ɵɵtemplate(18,Ke,1,1,"tb-example-hint",12)(19,Ue,5,6,"div",13),t.ɵɵelementEnd(),t.ɵɵelementStart(20,"section",14)(21,"mat-expansion-panel",15)(22,"mat-expansion-panel-header",16)(23,"mat-panel-title",5),t.ɵɵtext(24,"tb.rulenode.advanced-settings"),t.ɵɵelementEnd()(),t.ɵɵelementStart(25,"div",17)(26,"div",18),t.ɵɵpipe(27,"translate"),t.ɵɵelementStart(28,"mat-slide-toggle",19),t.ɵɵtext(29),t.ɵɵpipe(30,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(31,"div",18),t.ɵɵpipe(32,"translate"),t.ɵɵelementStart(33,"mat-slide-toggle",20),t.ɵɵtext(34),t.ɵɵpipe(35,"translate"),t.ɵɵelementEnd()()()()()()),2&e&&(t.ɵɵproperty("formGroup",n.createRelationConfigForm),t.ɵɵadvance(9),t.ɵɵproperty("ngForOf",n.directionTypes),t.ɵɵadvance(6),t.ɵɵproperty("allowedEntityTypes",n.allowedEntityTypes),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.createRelationConfigForm.get("entityType").value&&n.createRelationConfigForm.get("entityType").value!==n.entityType.TENANT),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.createRelationConfigForm.get("entityType").value===n.entityType.DEVICE||n.createRelationConfigForm.get("entityType").value===n.entityType.ASSET),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.createRelationConfigForm.get("entityType").value===n.entityType.CUSTOMER||n.createRelationConfigForm.get("entityType").value===n.entityType.DEVICE||n.createRelationConfigForm.get("entityType").value===n.entityType.ASSET),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.createRelationConfigForm.get("entityType").value===n.entityType.CUSTOMER||n.createRelationConfigForm.get("entityType").value===n.entityType.DEVICE||n.createRelationConfigForm.get("entityType").value===n.entityType.ASSET),t.ɵɵadvance(7),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(27,11,"tb.rulenode.remove-current-relations-hint")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(30,13,"tb.rulenode.remove-current-relations")," "),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(32,15,"tb.rulenode.change-originator-to-related-entity-hint")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(35,17,"tb.rulenode.change-originator-to-related-entity")," "))},dependencies:t.ɵɵgetComponentDepsFactory(He),encapsulation:2})}}function ze(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",13),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.directionTypeTranslations.get(e))," ")}}function $e(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",18)(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",19),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,1,e.entityTypeNamePatternTranslation.get(e.deleteRelationConfigForm.get("entityType").value)))}}function Qe(e,n){1&e&&t.ɵɵelement(0,"tb-example-hint",20),2&e&&t.ɵɵproperty("hintText","tb.rulenode.kv-map-single-pattern-hint")}function Je(e,n){if(1&e&&(t.ɵɵelementStart(0,"div")(1,"div",14),t.ɵɵelement(2,"tb-entity-type-select",15),t.ɵɵtemplate(3,$e,5,3,"mat-form-field",16),t.ɵɵelementEnd(),t.ɵɵtemplate(4,Qe,1,1,"tb-example-hint",17),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵproperty("allowedEntityTypes",e.allowedEntityTypes),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.deleteRelationConfigForm.get("entityType").value&&e.deleteRelationConfigForm.get("entityType").value!==e.entityType.TENANT),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.deleteRelationConfigForm.get("entityType").value&&e.deleteRelationConfigForm.get("entityType").value!==e.entityType.TENANT)}}e("CreateRelationConfigComponent",He);class Ye extends i{constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(d),this.directionTypeTranslations=new Map([[d.FROM,"tb.rulenode.del-relation-direction-from"],[d.TO,"tb.rulenode.del-relation-direction-to"]]),this.entityTypeNamePatternTranslation=new Map([[u.DEVICE,"tb.rulenode.device-name-pattern"],[u.ASSET,"tb.rulenode.asset-name-pattern"],[u.ENTITY_VIEW,"tb.rulenode.entity-view-name-pattern"],[u.CUSTOMER,"tb.rulenode.customer-title-pattern"],[u.USER,"tb.rulenode.user-name-pattern"],[u.DASHBOARD,"tb.rulenode.dashboard-name-pattern"],[u.EDGE,"tb.rulenode.edge-name-pattern"]]),this.entityType=u,this.allowedEntityTypes=[u.DEVICE,u.ASSET,u.ENTITY_VIEW,u.TENANT,u.CUSTOMER,u.USER,u.DASHBOARD,u.EDGE]}configForm(){return this.deleteRelationConfigForm}onConfigurationSet(e){this.deleteRelationConfigForm=this.fb.group({deleteForSingleEntity:[!!e&&e.deleteForSingleEntity,[]],direction:[e?e.direction:null,[N.required]],entityType:[e?e.entityType:null,[]],entityNamePattern:[e?e.entityNamePattern:null,[]],relationType:[e?e.relationType:null,[N.required]]})}validatorTriggers(){return["deleteForSingleEntity","entityType"]}updateValidators(e){const t=this.deleteRelationConfigForm.get("deleteForSingleEntity").value,n=this.deleteRelationConfigForm.get("entityType").value;t?this.deleteRelationConfigForm.get("entityType").setValidators([N.required]):this.deleteRelationConfigForm.get("entityType").setValidators([]),t&&n&&n!==u.TENANT?this.deleteRelationConfigForm.get("entityNamePattern").setValidators([N.required,N.pattern(/.*\S.*/)]):this.deleteRelationConfigForm.get("entityNamePattern").setValidators([]),this.deleteRelationConfigForm.get("entityType").updateValueAndValidity({emitEvent:!1}),this.deleteRelationConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e})}prepareOutputConfig(e){return e.entityNamePattern=e.entityNamePattern?e.entityNamePattern.trim():null,e}static{this.ɵfac=function(e){return new(e||Ye)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Ye,selectors:[["tb-action-node-delete-relation-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:18,vars:9,consts:[[1,"tb-form-panel","no-padding","no-border",3,"formGroup"],[1,"tb-form-panel","stroked","no-padding-bottom"],["translate","",1,"tb-form-panel-title"],[1,"flex","flex-col"],["hideRequiredMarker","",1,"mat-block"],["translate",""],["required","","matInput","","formControlName","direction"],[3,"value",4,"ngFor","ngForOf"],["required","","formControlName","relationType"],[1,"tb-form-panel","stroked"],[1,"tb-form-row","no-border","no-padding",3,"tb-hint-tooltip-icon"],["formControlName","deleteForSingleEntity",1,"mat-slide"],[4,"ngIf"],[3,"value"],[1,"flex","flex-row","gap-2.5"],["showLabel","","required","","formControlName","entityType",1,"flex-1",3,"allowedEntityTypes"],["class","mat-block flex-1",4,"ngIf"],[3,"hintText",4,"ngIf"],[1,"mat-block","flex-1"],["required","","matInput","","formControlName","entityNamePattern"],[3,"hintText"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3,"tb.rulenode.relation-parameters"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",3)(5,"mat-form-field",4)(6,"mat-label",5),t.ɵɵtext(7,"relation.direction"),t.ɵɵelementEnd(),t.ɵɵelementStart(8,"mat-select",6),t.ɵɵtemplate(9,ze,3,4,"mat-option",7),t.ɵɵelementEnd()(),t.ɵɵelement(10,"tb-relation-type-autocomplete",8),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"div",9)(12,"div",10),t.ɵɵpipe(13,"translate"),t.ɵɵelementStart(14,"mat-slide-toggle",11),t.ɵɵtext(15),t.ɵɵpipe(16,"translate"),t.ɵɵelementEnd()(),t.ɵɵtemplate(17,Je,5,3,"div",12),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.deleteRelationConfigForm),t.ɵɵadvance(9),t.ɵɵproperty("ngForOf",n.directionTypes),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(13,5,"tb.rulenode.delete-relation-with-specific-entity-hint")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(16,7,"tb.rulenode.delete-relation-with-specific-entity")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.deleteRelationConfigForm.get("deleteForSingleEntity").value))},dependencies:t.ɵɵgetComponentDepsFactory(Ye),encapsulation:2})}}e("DeleteRelationConfigComponent",Ye);class We extends i{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.deviceProfile}onConfigurationSet(e){this.deviceProfile=this.fb.group({persistAlarmRulesState:[!!e&&e.persistAlarmRulesState],fetchAlarmRulesStateOnStart:[!!e&&e.fetchAlarmRulesStateOnStart]})}validatorTriggers(){return["persistAlarmRulesState"]}updateValidators(e){this.deviceProfile.get("persistAlarmRulesState").value?this.deviceProfile.get("fetchAlarmRulesStateOnStart").enable({emitEvent:!1}):(this.deviceProfile.get("fetchAlarmRulesStateOnStart").setValue(!1,{emitEvent:!1}),this.deviceProfile.get("fetchAlarmRulesStateOnStart").disable({emitEvent:!1})),this.deviceProfile.get("fetchAlarmRulesStateOnStart").updateValueAndValidity({emitEvent:e})}static{this.ɵfac=function(e){return new(e||We)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:We,selectors:[["tb-device-profile-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:13,vars:13,consts:[[1,"tb-form-panel","stroked",3,"formGroup"],["translate","",1,"tb-form-hint","tb-primary-fill"],[1,"tb-form-row","no-border","no-padding","slide-toggle",3,"tb-hint-tooltip-icon"],["formControlName","persistAlarmRulesState",1,"mat-slide"],["formControlName","fetchAlarmRulesStateOnStart",1,"mat-slide"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1),t.ɵɵtext(2,"tb.rulenode.device-profile-node-hint"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"div",2),t.ɵɵpipe(4,"translate"),t.ɵɵelementStart(5,"mat-slide-toggle",3),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(8,"div",2),t.ɵɵpipe(9,"translate"),t.ɵɵelementStart(10,"mat-slide-toggle",4),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.deviceProfile),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(4,5,"tb.rulenode.persist-alarm-rules-hint")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(7,7,"tb.rulenode.persist-alarm-rules")," "),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(9,9,"tb.rulenode.fetch-alarm-rules-hint")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(12,11,"tb.rulenode.fetch-alarm-rules")," "))},dependencies:t.ɵɵgetComponentDepsFactory(We),encapsulation:2})}}e("DeviceProfileConfigComponent",We);const Xe=["jsFuncComponent"],Ze=["tbelFuncComponent"],et=()=>["prevMsg","prevMetadata","prevMsgType"];function tt(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.message-count-required")," "))}function nt(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.min-message-count-message")," "))}function rt(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.generation-frequency-required")," "))}function at(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.min-generation-frequency-message")," "))}function it(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-select",22)(1,"tb-toggle-option",23),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"tb-toggle-option",23),t.ɵɵtext(5),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(),t.ɵɵproperty("value",e.scriptLanguage.TBEL),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,4,"tb.rulenode.script-lang-tbel")," "),t.ɵɵadvance(2),t.ɵɵproperty("value",e.scriptLanguage.JS),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(6,6,"tb.rulenode.script-lang-js")," ")}}function ot(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-js-func",18,0),t.ɵɵtemplate(2,it,7,8,"tb-toggle-select",19),t.ɵɵelementStart(3,"button",20),t.ɵɵpipe(4,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.testScript())})),t.ɵɵelementStart(5,"mat-icon",21),t.ɵɵtext(6,"bug_report"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("functionArgs",t.ɵɵpureFunction0(5,et)),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.tbelEnabled),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(4,3,e.testScriptLabel))}}function lt(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-js-func",24,1)(2,"tb-toggle-select",22)(3,"tb-toggle-option",23),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"tb-toggle-option",23),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"button",20),t.ɵɵpipe(10,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.testScript())})),t.ɵɵelementStart(11,"mat-icon",21),t.ɵɵtext(12,"bug_report"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("functionArgs",t.ɵɵpureFunction0(14,et))("disableUndefinedCheck",!0)("scriptLanguage",e.scriptLanguage.TBEL),t.ɵɵadvance(3),t.ɵɵproperty("value",e.scriptLanguage.TBEL),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(5,8,"tb.rulenode.script-lang-tbel")," "),t.ɵɵadvance(2),t.ɵɵproperty("value",e.scriptLanguage.JS),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(8,10,"tb.rulenode.script-lang-js")," "),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(10,12,e.testScriptLabel))}}class st extends i{constructor(e,t,r,a){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=r,this.translate=a,this.tbelEnabled=D(this.store).tbelEnabled,this.scriptLanguage=s,this.changeScript=new n,this.allowedEntityTypes=[u.DEVICE,u.ASSET,u.ENTITY_VIEW,u.CUSTOMER,u.USER,u.DASHBOARD],this.additionEntityTypes={TENANT:this.translate.instant("tb.rulenode.current-tenant"),RULE_NODE:this.translate.instant("tb.rulenode.current-rule-node")},this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-generator-function"}configForm(){return this.generatorConfigForm}onConfigurationSet(e){this.generatorConfigForm=this.fb.group({msgCount:[e?e.msgCount:null,[N.required,N.min(0)]],periodInSeconds:[e?e.periodInSeconds:null,[N.required,N.min(1)]],originator:[e?e.originator:{id:null,entityType:u.RULE_NODE},[]],scriptLang:[e?e.scriptLang:s.JS,[N.required]],jsScript:[e?e.jsScript:null,[]],tbelScript:[e?e.tbelScript:null,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.generatorConfigForm.get("scriptLang").value;t!==s.TBEL||this.tbelEnabled||(t=s.JS,this.generatorConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.generatorConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.generatorConfigForm.get("jsScript").setValidators(t===s.JS?[N.required]:[]),this.generatorConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.generatorConfigForm.get("tbelScript").setValidators(t===s.TBEL?[N.required]:[]),this.generatorConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return{msgCount:P(e?.msgCount)?e?.msgCount:0,periodInSeconds:P(e?.periodInSeconds)?e?.periodInSeconds:1,originator:{id:P(e?.originatorId)?e?.originatorId:null,entityType:P(e?.originatorType)?e?.originatorType:u.RULE_NODE},scriptLang:P(e?.scriptLang)?e?.scriptLang:s.JS,tbelScript:P(e?.tbelScript)?e?.tbelScript:null,jsScript:P(e?.jsScript)?e?.jsScript:null}}prepareOutputConfig(e){return e.originator?(e.originatorId=e.originator.id,e.originatorType=e.originator.entityType):(e.originatorId=null,e.originatorType=null),delete e.originator,e}testScript(e){const t=this.generatorConfigForm.get("scriptLang").value,n=t===s.JS?"jsScript":"tbelScript",r=t===s.JS?"rulenode/generator_node_script_fn":"rulenode/tbel/generator_node_script_fn",a=this.generatorConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(a,"generate",this.translate.instant("tb.rulenode.generator"),"Generate",["prevMsg","prevMetadata","prevMsgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.generatorConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.generatorConfigForm.get("scriptLang").value===s.JS&&this.jsFuncComponent.validateOnSubmit()}static{this.ɵfac=function(e){return new(e||st)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder),t.ɵɵdirectiveInject(L.NodeScriptTestService),t.ɵɵdirectiveInject(K.TranslateService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:st,selectors:[["tb-action-node-generator-config"]],viewQuery:function(e,n){if(1&e&&(t.ɵɵviewQuery(Xe,5),t.ɵɵviewQuery(Ze,5)),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.jsFuncComponent=e.first),t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.tbelFuncComponent=e.first)}},features:[t.ɵɵInheritDefinitionFeature],decls:32,vars:12,consts:[["jsFuncComponent",""],["tbelFuncComponent",""],[1,"tb-form-panel","no-border","no-padding",3,"formGroup"],[1,"tb-form-panel","no-padding-bottom","stroked"],["translate","",1,"tb-form-panel-title"],[1,"tb-form-row","no-border","no-padding","tb-standard-fields","column-xs"],[1,"flex"],["translate",""],["required","","type","number","min","0","step","1","matInput","","formControlName","msgCount"],[4,"ngIf"],["required","","type","number","min","1","step","1","matInput","","formControlName","periodInSeconds"],["required","true","useAliasEntityTypes","true","formControlName","originator",1,"flex-1",3,"allowedEntityTypes","additionEntityTypes"],[1,"tb-form-panel","stroked"],["expanded","",1,"tb-settings"],["formControlName","jsScript","functionName","Generate","helpId","rulenode/generator_node_script_fn","noValidate","true",3,"functionArgs",4,"ngIf"],["formControlName","tbelScript","functionName","Generate","helpId","rulenode/tbel/generator_node_script_fn","noValidate","true",3,"functionArgs","disableUndefinedCheck","scriptLanguage",4,"ngIf"],[1,"flex","flex-row",2,"padding-bottom","16px"],["mat-button","","mat-raised-button","","color","primary",3,"click"],["formControlName","jsScript","functionName","Generate","helpId","rulenode/generator_node_script_fn","noValidate","true",3,"functionArgs"],["toolbarPrefixButton","","formControlName","scriptLang","appearance","fill",4,"ngIf"],["toolbarSuffixButton","","mat-icon-button","","matTooltipPosition","above",1,"tb-mat-32",3,"click","matTooltip"],["color","primary",1,"material-icons"],["toolbarPrefixButton","","formControlName","scriptLang","appearance","fill"],[3,"value"],["formControlName","tbelScript","functionName","Generate","helpId","rulenode/tbel/generator_node_script_fn","noValidate","true",3,"functionArgs","disableUndefinedCheck","scriptLanguage"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",2)(1,"div",3)(2,"div",4),t.ɵɵtext(3,"tb.rulenode.generation-parameters"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",5)(5,"mat-form-field",6)(6,"mat-label",7),t.ɵɵtext(7,"tb.rulenode.message-count"),t.ɵɵelementEnd(),t.ɵɵelement(8,"input",8),t.ɵɵtemplate(9,tt,3,3,"mat-error",9)(10,nt,3,3,"mat-error",9),t.ɵɵelementEnd(),t.ɵɵelementStart(11,"mat-form-field",6)(12,"mat-label",7),t.ɵɵtext(13,"tb.rulenode.generation-frequency-seconds"),t.ɵɵelementEnd(),t.ɵɵelement(14,"input",10),t.ɵɵtemplate(15,rt,3,3,"mat-error",9)(16,at,3,3,"mat-error",9),t.ɵɵelementEnd()()(),t.ɵɵelementStart(17,"div",3)(18,"div",4),t.ɵɵtext(19,"tb.rulenode.originator"),t.ɵɵelementEnd(),t.ɵɵelement(20,"tb-entity-select",11),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"div",12)(22,"mat-expansion-panel",13)(23,"mat-expansion-panel-header")(24,"mat-panel-title",7),t.ɵɵtext(25,"tb.rulenode.generator-function"),t.ɵɵelementEnd()(),t.ɵɵtemplate(26,ot,7,6,"tb-js-func",14)(27,lt,13,15,"tb-js-func",15),t.ɵɵelementStart(28,"div",16)(29,"button",17),t.ɵɵlistener("click",(function(){return n.testScript()})),t.ɵɵtext(30),t.ɵɵpipe(31,"translate"),t.ɵɵelementEnd()()()()()),2&e&&(t.ɵɵproperty("formGroup",n.generatorConfigForm),t.ɵɵadvance(9),t.ɵɵproperty("ngIf",n.generatorConfigForm.get("msgCount").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.generatorConfigForm.get("msgCount").hasError("min")),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.generatorConfigForm.get("periodInSeconds").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.generatorConfigForm.get("periodInSeconds").hasError("min")),t.ɵɵadvance(4),t.ɵɵproperty("allowedEntityTypes",n.allowedEntityTypes)("additionEntityTypes",n.additionEntityTypes),t.ɵɵadvance(6),t.ɵɵproperty("ngIf",n.generatorConfigForm.get("scriptLang").value===n.scriptLanguage.JS),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.generatorConfigForm.get("scriptLang").value===n.scriptLanguage.TBEL),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(31,10,n.testScriptLabel)," "))},dependencies:t.ɵɵgetComponentDepsFactory(st),styles:["[_nghost-%COMP%] .mat-button-toggle-group{min-width:120px;height:24px!important}[_nghost-%COMP%] .mat-button-toggle-group .mat-button-toggle{font-size:0}[_nghost-%COMP%] .mat-button-toggle-group .mat-button-toggle .mat-button-toggle-button{height:20px!important;line-height:20px!important;border:none!important}[_nghost-%COMP%] .mat-button-toggle-group .mat-button-toggle .mat-button-toggle-button .mat-button-toggle-label-content{font-size:14px!important;line-height:20px!important}@media screen and (min-width: 599px){[_nghost-%COMP%] .tb-entity-select{display:flex;flex-direction:row;gap:16px}}[_nghost-%COMP%] .tb-entity-select tb-entity-type-select{flex:1}[_nghost-%COMP%] .tb-entity-select tb-entity-autocomplete{flex:1}[_nghost-%COMP%] .tb-entity-select tb-entity-autocomplete mat-form-field{width:100%!important}"]})}}var pt;e("GeneratorConfigComponent",st),function(e){e.CUSTOMER="CUSTOMER",e.TENANT="TENANT",e.RELATED="RELATED",e.ALARM_ORIGINATOR="ALARM_ORIGINATOR",e.ENTITY="ENTITY"}(pt||(pt={}));const mt=new Map([[pt.CUSTOMER,"tb.rulenode.originator-customer"],[pt.TENANT,"tb.rulenode.originator-tenant"],[pt.RELATED,"tb.rulenode.originator-related"],[pt.ALARM_ORIGINATOR,"tb.rulenode.originator-alarm-originator"],[pt.ENTITY,"tb.rulenode.originator-entity"]]),dt=new Map([[pt.CUSTOMER,"tb.rulenode.originator-customer-desc"],[pt.TENANT,"tb.rulenode.originator-tenant-desc"],[pt.RELATED,"tb.rulenode.originator-related-entity-desc"],[pt.ALARM_ORIGINATOR,"tb.rulenode.originator-alarm-originator-desc"],[pt.ENTITY,"tb.rulenode.originator-entity-by-name-pattern-desc"]]),ut=[c.createdTime,c.name,{value:"type",name:"tb.rulenode.profile-name",keyName:"originatorProfileName"},c.firstName,c.lastName,c.email,c.title,c.country,c.state,c.city,c.address,c.address2,c.zip,c.phone,c.label,{value:"id",name:"tb.rulenode.id",keyName:"id"},{value:"additionalInfo",name:"tb.rulenode.additional-info",keyName:"additionalInfo"}],ct=new Map([["type","profileName"],["createdTime","createdTime"],["name","name"],["firstName","firstName"],["lastName","lastName"],["email","email"],["title","title"],["country","country"],["state","state"],["city","city"],["address","address"],["address2","address2"],["zip","zip"],["phone","phone"],["label","label"],["id","id"],["additionalInfo","additionalInfo"]]);var ft;!function(e){e.CIRCLE="CIRCLE",e.POLYGON="POLYGON"}(ft||(ft={}));const gt=new Map([[ft.CIRCLE,"tb.rulenode.perimeter-circle"],[ft.POLYGON,"tb.rulenode.perimeter-polygon"]]);var ht;!function(e){e.MILLISECONDS="MILLISECONDS",e.SECONDS="SECONDS",e.MINUTES="MINUTES",e.HOURS="HOURS",e.DAYS="DAYS"}(ht||(ht={}));const yt=new Map([[ht.MILLISECONDS,"tb.rulenode.time-unit-milliseconds"],[ht.SECONDS,"tb.rulenode.time-unit-seconds"],[ht.MINUTES,"tb.rulenode.time-unit-minutes"],[ht.HOURS,"tb.rulenode.time-unit-hours"],[ht.DAYS,"tb.rulenode.time-unit-days"]]);var bt;!function(e){e.METER="METER",e.KILOMETER="KILOMETER",e.FOOT="FOOT",e.MILE="MILE",e.NAUTICAL_MILE="NAUTICAL_MILE"}(bt||(bt={}));const vt=new Map([[bt.METER,"tb.rulenode.range-unit-meter"],[bt.KILOMETER,"tb.rulenode.range-unit-kilometer"],[bt.FOOT,"tb.rulenode.range-unit-foot"],[bt.MILE,"tb.rulenode.range-unit-mile"],[bt.NAUTICAL_MILE,"tb.rulenode.range-unit-nautical-mile"]]);var xt;!function(e){e.ID="ID",e.TITLE="TITLE",e.COUNTRY="COUNTRY",e.STATE="STATE",e.CITY="CITY",e.ZIP="ZIP",e.ADDRESS="ADDRESS",e.ADDRESS2="ADDRESS2",e.PHONE="PHONE",e.EMAIL="EMAIL",e.ADDITIONAL_INFO="ADDITIONAL_INFO"}(xt||(xt={}));const Ct=new Map([[xt.ID,"tb.rulenode.entity-details-id"],[xt.TITLE,"tb.rulenode.entity-details-title"],[xt.COUNTRY,"tb.rulenode.entity-details-country"],[xt.STATE,"tb.rulenode.entity-details-state"],[xt.CITY,"tb.rulenode.entity-details-city"],[xt.ZIP,"tb.rulenode.entity-details-zip"],[xt.ADDRESS,"tb.rulenode.entity-details-address"],[xt.ADDRESS2,"tb.rulenode.entity-details-address2"],[xt.PHONE,"tb.rulenode.entity-details-phone"],[xt.EMAIL,"tb.rulenode.entity-details-email"],[xt.ADDITIONAL_INFO,"tb.rulenode.entity-details-additional_info"]]);var St;!function(e){e.FIRST="FIRST",e.LAST="LAST",e.ALL="ALL"}(St||(St={}));const Tt=new Map([[St.FIRST,"tb.rulenode.first"],[St.LAST,"tb.rulenode.last"],[St.ALL,"tb.rulenode.all"]]),It=new Map([[St.FIRST,"tb.rulenode.first-mode-hint"],[St.LAST,"tb.rulenode.last-mode-hint"],[St.ALL,"tb.rulenode.all-mode-hint"]]);var Et,Ft;!function(e){e.ASC="ASC",e.DESC="DESC"}(Et||(Et={})),function(e){e.ATTRIBUTES="ATTRIBUTES",e.LATEST_TELEMETRY="LATEST_TELEMETRY",e.FIELDS="FIELDS"}(Ft||(Ft={}));const qt=new Map([[Ft.ATTRIBUTES,"tb.rulenode.attributes"],[Ft.LATEST_TELEMETRY,"tb.rulenode.latest-telemetry"],[Ft.FIELDS,"tb.rulenode.fields"]]),At=new Map([[Ft.ATTRIBUTES,"tb.rulenode.add-mapped-attribute-to"],[Ft.LATEST_TELEMETRY,"tb.rulenode.add-mapped-latest-telemetry-to"],[Ft.FIELDS,"tb.rulenode.add-mapped-fields-to"]]),kt=new Map([[Et.ASC,"tb.rulenode.ascending"],[Et.DESC,"tb.rulenode.descending"]]);var Nt;!function(e){e.STANDARD="STANDARD",e.FIFO="FIFO"}(Nt||(Nt={}));const wt=new Map([[Nt.STANDARD,"tb.rulenode.sqs-queue-standard"],[Nt.FIFO,"tb.rulenode.sqs-queue-fifo"]]),Mt=["anonymous","basic","cert.PEM"],Bt=new Map([["anonymous","tb.rulenode.credentials-anonymous"],["basic","tb.rulenode.credentials-basic"],["cert.PEM","tb.rulenode.credentials-pem"]]),Vt=["sas","cert.PEM"],Ot=new Map([["sas","tb.rulenode.credentials-sas"],["cert.PEM","tb.rulenode.credentials-pem"]]);var Dt;!function(e){e.GET="GET",e.POST="POST",e.PUT="PUT",e.DELETE="DELETE"}(Dt||(Dt={}));const Lt=["US-ASCII","ISO-8859-1","UTF-8","UTF-16BE","UTF-16LE","UTF-16"],Pt=new Map([["US-ASCII","tb.rulenode.charset-us-ascii"],["ISO-8859-1","tb.rulenode.charset-iso-8859-1"],["UTF-8","tb.rulenode.charset-utf-8"],["UTF-16BE","tb.rulenode.charset-utf-16be"],["UTF-16LE","tb.rulenode.charset-utf-16le"],["UTF-16","tb.rulenode.charset-utf-16"]]);var Rt;!function(e){e.CUSTOM="CUSTOM",e.ADD="ADD",e.SUB="SUB",e.MULT="MULT",e.DIV="DIV",e.SIN="SIN",e.SINH="SINH",e.COS="COS",e.COSH="COSH",e.TAN="TAN",e.TANH="TANH",e.ACOS="ACOS",e.ASIN="ASIN",e.ATAN="ATAN",e.ATAN2="ATAN2",e.EXP="EXP",e.EXPM1="EXPM1",e.SQRT="SQRT",e.CBRT="CBRT",e.GET_EXP="GET_EXP",e.HYPOT="HYPOT",e.LOG="LOG",e.LOG10="LOG10",e.LOG1P="LOG1P",e.CEIL="CEIL",e.FLOOR="FLOOR",e.FLOOR_DIV="FLOOR_DIV",e.FLOOR_MOD="FLOOR_MOD",e.ABS="ABS",e.MIN="MIN",e.MAX="MAX",e.POW="POW",e.SIGNUM="SIGNUM",e.RAD="RAD",e.DEG="DEG"}(Rt||(Rt={}));const _t=new Map([[Rt.CUSTOM,{value:Rt.CUSTOM,name:"Custom Function",description:"Use this function to specify complex mathematical expression.",minArgs:1,maxArgs:16}],[Rt.ADD,{value:Rt.ADD,name:"Addition",description:"x + y",minArgs:2,maxArgs:2}],[Rt.SUB,{value:Rt.SUB,name:"Subtraction",description:"x - y",minArgs:2,maxArgs:2}],[Rt.MULT,{value:Rt.MULT,name:"Multiplication",description:"x * y",minArgs:2,maxArgs:2}],[Rt.DIV,{value:Rt.DIV,name:"Division",description:"x / y",minArgs:2,maxArgs:2}],[Rt.SIN,{value:Rt.SIN,name:"Sine",description:"Returns the trigonometric sine of an angle in radians.",minArgs:1,maxArgs:1}],[Rt.SINH,{value:Rt.SINH,name:"Hyperbolic sine",description:"Returns the hyperbolic sine of an argument.",minArgs:1,maxArgs:1}],[Rt.COS,{value:Rt.COS,name:"Cosine",description:"Returns the trigonometric cosine of an angle in radians.",minArgs:1,maxArgs:1}],[Rt.COSH,{value:Rt.COSH,name:"Hyperbolic cosine",description:"Returns the hyperbolic cosine of an argument.",minArgs:1,maxArgs:1}],[Rt.TAN,{value:Rt.TAN,name:"Tangent",description:"Returns the trigonometric tangent of an angle in radians",minArgs:1,maxArgs:1}],[Rt.TANH,{value:Rt.TANH,name:"Hyperbolic tangent",description:"Returns the hyperbolic tangent of an argument",minArgs:1,maxArgs:1}],[Rt.ACOS,{value:Rt.ACOS,name:"Arc cosine",description:"Returns the arc cosine of an argument",minArgs:1,maxArgs:1}],[Rt.ASIN,{value:Rt.ASIN,name:"Arc sine",description:"Returns the arc sine of an argument",minArgs:1,maxArgs:1}],[Rt.ATAN,{value:Rt.ATAN,name:"Arc tangent",description:"Returns the arc tangent of an argument",minArgs:1,maxArgs:1}],[Rt.ATAN2,{value:Rt.ATAN2,name:"2-argument arc tangent",description:"Returns the angle theta from the conversion of rectangular coordinates (x, y) to polar coordinates (r, theta)",minArgs:2,maxArgs:2}],[Rt.EXP,{value:Rt.EXP,name:"Exponential",description:"Returns Euler's number e raised to the power of an argument",minArgs:1,maxArgs:1}],[Rt.EXPM1,{value:Rt.EXPM1,name:"Exponential minus one",description:"Returns Euler's number e raised to the power of an argument minus one",minArgs:1,maxArgs:1}],[Rt.SQRT,{value:Rt.SQRT,name:"Square",description:"Returns the correctly rounded positive square root of an argument",minArgs:1,maxArgs:1}],[Rt.CBRT,{value:Rt.CBRT,name:"Cube root",description:"Returns the cube root of an argument",minArgs:1,maxArgs:1}],[Rt.GET_EXP,{value:Rt.GET_EXP,name:"Get exponent",description:"Returns the unbiased exponent used in the representation of an argument",minArgs:1,maxArgs:1}],[Rt.HYPOT,{value:Rt.HYPOT,name:"Square root",description:"Returns the square root of the squares of the arguments",minArgs:2,maxArgs:2}],[Rt.LOG,{value:Rt.LOG,name:"Logarithm",description:"Returns the natural logarithm of an argument",minArgs:1,maxArgs:1}],[Rt.LOG10,{value:Rt.LOG10,name:"Base 10 logarithm",description:"Returns the base 10 logarithm of an argument",minArgs:1,maxArgs:1}],[Rt.LOG1P,{value:Rt.LOG1P,name:"Logarithm of the sum",description:"Returns the natural logarithm of the sum of an argument",minArgs:1,maxArgs:1}],[Rt.CEIL,{value:Rt.CEIL,name:"Ceiling",description:"Returns the smallest (closest to negative infinity) of an argument",minArgs:1,maxArgs:1}],[Rt.FLOOR,{value:Rt.FLOOR,name:"Floor",description:"Returns the largest (closest to positive infinity) of an argument",minArgs:1,maxArgs:1}],[Rt.FLOOR_DIV,{value:Rt.FLOOR_DIV,name:"Floor division",description:"Returns the largest (closest to positive infinity) of the arguments",minArgs:2,maxArgs:2}],[Rt.FLOOR_MOD,{value:Rt.FLOOR_MOD,name:"Floor modulus",description:"Returns the floor modulus of the arguments",minArgs:2,maxArgs:2}],[Rt.ABS,{value:Rt.ABS,name:"Absolute",description:"Returns the absolute value of an argument",minArgs:1,maxArgs:1}],[Rt.MIN,{value:Rt.MIN,name:"Min",description:"Returns the smaller of the arguments",minArgs:2,maxArgs:2}],[Rt.MAX,{value:Rt.MAX,name:"Max",description:"Returns the greater of the arguments",minArgs:2,maxArgs:2}],[Rt.POW,{value:Rt.POW,name:"Raise to a power",description:"Returns the value of the first argument raised to the power of the second argument",minArgs:2,maxArgs:2}],[Rt.SIGNUM,{value:Rt.SIGNUM,name:"Sign of a real number",description:"Returns the signum function of the argument",minArgs:1,maxArgs:1}],[Rt.RAD,{value:Rt.RAD,name:"Radian",description:"Converts an angle measured in degrees to an approximately equivalent angle measured in radians",minArgs:1,maxArgs:1}],[Rt.DEG,{value:Rt.DEG,name:"Degrees",description:"Converts an angle measured in radians to an approximately equivalent angle measured in degrees.",minArgs:1,maxArgs:1}]]);var jt,Gt,Kt;!function(e){e.MESSAGE_BODY="MESSAGE_BODY",e.MESSAGE_METADATA="MESSAGE_METADATA",e.ATTRIBUTE="ATTRIBUTE",e.TIME_SERIES="TIME_SERIES",e.CONSTANT="CONSTANT"}(jt||(jt={})),function(e){e.MESSAGE_BODY="MESSAGE_BODY",e.MESSAGE_METADATA="MESSAGE_METADATA",e.ATTRIBUTE="ATTRIBUTE",e.TIME_SERIES="TIME_SERIES"}(Gt||(Gt={})),function(e){e.DATA="DATA",e.METADATA="METADATA"}(Kt||(Kt={}));const Ut=new Map([[Kt.DATA,"tb.rulenode.message-to-metadata"],[Kt.METADATA,"tb.rulenode.metadata-to-message"]]),Ht=(new Map([[Kt.DATA,"tb.rulenode.from-message"],[Kt.METADATA,"tb.rulenode.from-metadata"]]),new Map([[Kt.DATA,"tb.rulenode.message"],[Kt.METADATA,"tb.rulenode.metadata"]])),zt=new Map([[Kt.DATA,"tb.rulenode.message"],[Kt.METADATA,"tb.rulenode.message-metadata"]]),$t=new Map([[jt.MESSAGE_BODY,{name:"tb.rulenode.message-body-type",description:"Fetch argument value from incoming message"}],[jt.MESSAGE_METADATA,{name:"tb.rulenode.message-metadata-type",description:"Fetch argument value from incoming message metadata"}],[jt.ATTRIBUTE,{name:"tb.rulenode.attribute-type",description:"Fetch attribute value from database"}],[jt.TIME_SERIES,{name:"tb.rulenode.time-series-type",description:"Fetch latest time-series value from database"}],[jt.CONSTANT,{name:"tb.rulenode.constant-type",description:"Define constant value"}]]),Qt=new Map([[Gt.MESSAGE_BODY,{name:"tb.rulenode.message-body-type",description:"Add result to the outgoing message"}],[Gt.MESSAGE_METADATA,{name:"tb.rulenode.message-metadata-type",description:"Add result to the outgoing message metadata"}],[Gt.ATTRIBUTE,{name:"tb.rulenode.attribute-type",description:"Store result as an entity attribute in the database"}],[Gt.TIME_SERIES,{name:"tb.rulenode.time-series-type",description:"Store result as an entity time-series in the database"}]]),Jt=["x","y","z","a","b","c","d","k","l","m","n","o","p","r","s","t"];var Yt,Wt;!function(e){e.SHARED_SCOPE="SHARED_SCOPE",e.SERVER_SCOPE="SERVER_SCOPE",e.CLIENT_SCOPE="CLIENT_SCOPE"}(Yt||(Yt={})),function(e){e.SHARED_SCOPE="SHARED_SCOPE",e.SERVER_SCOPE="SERVER_SCOPE"}(Wt||(Wt={}));const Xt=new Map([[Yt.SHARED_SCOPE,"tb.rulenode.shared-scope"],[Yt.SERVER_SCOPE,"tb.rulenode.server-scope"],[Yt.CLIENT_SCOPE,"tb.rulenode.client-scope"]]);var Zt;!function(e){e.ON_FIRST_MESSAGE="ON_FIRST_MESSAGE",e.ON_EACH_MESSAGE="ON_EACH_MESSAGE"}(Zt||(Zt={}));const en=new Map([[Zt.ON_EACH_MESSAGE,{value:!0,name:"tb.rulenode.presence-monitoring-strategy-on-each-message"}],[Zt.ON_FIRST_MESSAGE,{value:!1,name:"tb.rulenode.presence-monitoring-strategy-on-first-message"}]]),tn=2147483648,nn=e=>({perimeterKeyName:e});function rn(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.latitude-field-name-required")," "))}function an(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.longitude-field-name-required")," "))}function on(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",22),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.perimeterTypeTranslationMap.get(e))," ")}}function ln(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.perimeter-key-name-required")," "))}function sn(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",23)(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",24),t.ɵɵtemplate(5,ln,3,3,"mat-error",6),t.ɵɵelementStart(6,"mat-hint"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,3,"tb.rulenode.perimeter-key-name")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",e.geoActionConfigForm.get("perimeterKeyName").hasError("required")),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(8,5,"tb.rulenode.perimeter-key-name-hint"))}}function pn(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.circle-center-latitude-required")," "))}function mn(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.circle-center-longitude-required")," "))}function dn(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.range-required")," "))}function un(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",22),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.rangeUnitTranslationMap.get(e))," ")}}function cn(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.range-units-required")," "))}function fn(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",9)(1,"div",3)(2,"mat-form-field",25)(3,"mat-label"),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(6,"input",26),t.ɵɵtemplate(7,pn,3,3,"mat-error",6),t.ɵɵelementEnd(),t.ɵɵelementStart(8,"mat-form-field",25)(9,"mat-label"),t.ɵɵtext(10),t.ɵɵpipe(11,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(12,"input",27),t.ɵɵtemplate(13,mn,3,3,"mat-error",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(14,"div",3)(15,"mat-form-field",25)(16,"mat-label"),t.ɵɵtext(17),t.ɵɵpipe(18,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(19,"input",28),t.ɵɵtemplate(20,dn,3,3,"mat-error",6),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"mat-form-field",25)(22,"mat-label"),t.ɵɵtext(23),t.ɵɵpipe(24,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(25,"mat-select",29),t.ɵɵtemplate(26,un,3,4,"mat-option",12),t.ɵɵelementEnd(),t.ɵɵtemplate(27,cn,3,3,"mat-error",6),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(5,9,"tb.rulenode.circle-center-latitude")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",e.geoActionConfigForm.get("centerLatitude").hasError("required")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(11,11,"tb.rulenode.circle-center-longitude")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",e.geoActionConfigForm.get("centerLongitude").hasError("required")),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(18,13,"tb.rulenode.range")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",e.geoActionConfigForm.get("range").hasError("required")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(24,15,"tb.rulenode.range-units")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",e.rangeUnits),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.geoActionConfigForm.get("rangeUnit").hasError("required"))}}function gn(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.polygon-definition-required")," "))}function hn(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",9)(1,"mat-form-field",30)(2,"mat-label",31),t.ɵɵtext(3,"tb.rulenode.polygon-definition"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",32),t.ɵɵelementStart(5,"mat-icon",33),t.ɵɵpipe(6,"translate"),t.ɵɵtext(7," help "),t.ɵɵelementEnd(),t.ɵɵtemplate(8,gn,3,3,"mat-error",6),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(6,2,"tb.rulenode.polygon-definition-hint")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",e.geoActionConfigForm.get("polygonsDefinition").hasError("required"))}}function yn(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",22),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext();t.ɵɵproperty("value",r.presenceMonitoringStrategies.get(e).value),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.presenceMonitoringStrategies.get(e).name)," ")}}function bn(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.min-inside-duration-value-required")," "))}function vn(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.time-value-range")," "))}function xn(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.time-value-range")," "))}function Cn(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",22),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.timeUnitsTranslationMap.get(e))," ")}}function Sn(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.min-outside-duration-value-required")," "))}function Tn(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.time-value-range")," "))}function In(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.time-value-range")," "))}function En(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",22),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.timeUnitsTranslationMap.get(e))," ")}}function Fn(e,n){if(1&e&&(t.ɵɵelementStart(0,"div")(1,"div",34)(2,"mat-form-field",35)(3,"mat-label",31),t.ɵɵtext(4,"tb.rulenode.min-inside-duration"),t.ɵɵelementEnd(),t.ɵɵelement(5,"input",36),t.ɵɵtemplate(6,bn,3,3,"mat-error",6)(7,vn,3,3,"mat-error",6)(8,xn,3,3,"mat-error",6),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"mat-form-field",35)(10,"mat-label",31),t.ɵɵtext(11,"tb.rulenode.min-inside-duration-time-unit"),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"mat-select",37),t.ɵɵtemplate(13,Cn,3,4,"mat-option",12),t.ɵɵelementEnd()()(),t.ɵɵelementStart(14,"div",34)(15,"mat-form-field",35)(16,"mat-label",31),t.ɵɵtext(17,"tb.rulenode.min-outside-duration"),t.ɵɵelementEnd(),t.ɵɵelement(18,"input",38),t.ɵɵtemplate(19,Sn,3,3,"mat-error",6)(20,Tn,3,3,"mat-error",6)(21,In,3,3,"mat-error",6),t.ɵɵelementEnd(),t.ɵɵelementStart(22,"mat-form-field",35)(23,"mat-label",31),t.ɵɵtext(24,"tb.rulenode.min-outside-duration-time-unit"),t.ɵɵelementEnd(),t.ɵɵelementStart(25,"mat-select",39),t.ɵɵtemplate(26,En,3,4,"mat-option",12),t.ɵɵelementEnd()()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(6),t.ɵɵproperty("ngIf",e.geoActionConfigForm.get("minInsideDuration").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.geoActionConfigForm.get("minInsideDuration").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.geoActionConfigForm.get("minInsideDuration").hasError("max")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",e.timeUnits),t.ɵɵadvance(6),t.ɵɵproperty("ngIf",e.geoActionConfigForm.get("minOutsideDuration").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.geoActionConfigForm.get("minOutsideDuration").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.geoActionConfigForm.get("minOutsideDuration").hasError("max")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",e.timeUnits)}}class qn extends i{constructor(e,t){super(e),this.store=e,this.fb=t,this.perimeterType=ft,this.perimeterTypes=Object.keys(ft),this.perimeterTypeTranslationMap=gt,this.rangeUnits=Object.keys(bt),this.rangeUnitTranslationMap=vt,this.presenceMonitoringStrategies=en,this.presenceMonitoringStrategyKeys=Array.from(this.presenceMonitoringStrategies.keys()),this.timeUnits=Object.keys(ht),this.timeUnitsTranslationMap=yt,this.defaultPaddingEnable=!0}configForm(){return this.geoActionConfigForm}onConfigurationSet(e){this.geoActionConfigForm=this.fb.group({reportPresenceStatusOnEachMessage:[!e||e.reportPresenceStatusOnEachMessage,[N.required]],latitudeKeyName:[e?e.latitudeKeyName:null,[N.required]],longitudeKeyName:[e?e.longitudeKeyName:null,[N.required]],perimeterType:[e?e.perimeterType:null,[N.required]],fetchPerimeterInfoFromMessageMetadata:[!!e&&e.fetchPerimeterInfoFromMessageMetadata,[]],perimeterKeyName:[e?e.perimeterKeyName:null,[]],centerLatitude:[e?e.centerLatitude:null,[]],centerLongitude:[e?e.centerLatitude:null,[]],range:[e?e.range:null,[]],rangeUnit:[e?e.rangeUnit:null,[]],polygonsDefinition:[e?e.polygonsDefinition:null,[]],minInsideDuration:[e?e.minInsideDuration:null,[N.required,N.min(1),N.max(2147483647)]],minInsideDurationTimeUnit:[e?e.minInsideDurationTimeUnit:null,[N.required]],minOutsideDuration:[e?e.minOutsideDuration:null,[N.required,N.min(1),N.max(2147483647)]],minOutsideDurationTimeUnit:[e?e.minOutsideDurationTimeUnit:null,[N.required]]})}validatorTriggers(){return["fetchPerimeterInfoFromMessageMetadata","perimeterType"]}updateValidators(e){const t=this.geoActionConfigForm.get("fetchPerimeterInfoFromMessageMetadata").value,n=this.geoActionConfigForm.get("perimeterType").value;t?this.geoActionConfigForm.get("perimeterKeyName").setValidators([N.required]):this.geoActionConfigForm.get("perimeterKeyName").setValidators([]),t||n!==ft.CIRCLE?(this.geoActionConfigForm.get("centerLatitude").setValidators([]),this.geoActionConfigForm.get("centerLongitude").setValidators([]),this.geoActionConfigForm.get("range").setValidators([]),this.geoActionConfigForm.get("rangeUnit").setValidators([]),this.defaultPaddingEnable=!0):(this.geoActionConfigForm.get("centerLatitude").setValidators([N.required,N.min(-90),N.max(90)]),this.geoActionConfigForm.get("centerLongitude").setValidators([N.required,N.min(-180),N.max(180)]),this.geoActionConfigForm.get("range").setValidators([N.required,N.min(0)]),this.geoActionConfigForm.get("rangeUnit").setValidators([N.required]),this.defaultPaddingEnable=!1),t||n!==ft.POLYGON?this.geoActionConfigForm.get("polygonsDefinition").setValidators([]):this.geoActionConfigForm.get("polygonsDefinition").setValidators([N.required]),this.geoActionConfigForm.get("perimeterKeyName").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("centerLatitude").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("centerLongitude").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("range").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("rangeUnit").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("polygonsDefinition").updateValueAndValidity({emitEvent:e})}static{this.ɵfac=function(e){return new(e||qn)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:qn,selectors:[["tb-action-node-gps-geofencing-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:52,vars:42,consts:[[1,"tb-form-panel","no-border","no-padding",3,"formGroup"],[1,"tb-form-panel","stroked"],["translate","",1,"tb-form-panel-title"],[1,"flex","flex-row","gap-4"],[1,"mat-block","max-w-50%","flex-full"],["matInput","","formControlName","latitudeKeyName","required",""],[4,"ngIf"],["matInput","","formControlName","longitudeKeyName","required",""],["translate","",1,"tb-form-hint","tb-primary-fill"],[1,"flex","flex-col"],["hideRequiredMarker","",1,"mat-block","flex-1"],["formControlName","perimeterType"],[3,"value",4,"ngFor","ngForOf"],[1,"tb-form-row","no-border","no-padding","slide-toggle",3,"tb-hint-tooltip-icon"],["formControlName","fetchPerimeterInfoFromMessageMetadata",1,"mat-slide"],["class","mat-block",4,"ngIf"],["class","flex flex-col",4,"ngIf"],[1,"tb-form-panel","stroked","no-padding-bottom"],[1,"flex","flex-col","items-stretch","justify-between","gt-sm:flex-row","lt-md:gap-4"],[1,"tb-form-panel-title"],["formControlName","reportPresenceStatusOnEachMessage","appearance","fill",1,"fetch-to-data-toggle"],[1,"tb-form-hint","tb-primary-fill"],[3,"value"],[1,"mat-block"],["matInput","","formControlName","perimeterKeyName","required",""],[1,"flex-1"],["type","number","min","-90","max","90","step","0.1","matInput","","formControlName","centerLatitude","required",""],["type","number","min","-180","max","180","step","0.1","matInput","","formControlName","centerLongitude","required",""],["type","number","min","0","step","0.1","matInput","","formControlName","range","required",""],["formControlName","rangeUnit","required",""],["subscriptSizing","dynamic",1,"mat-block"],["translate",""],["matInput","","formControlName","polygonsDefinition","required",""],["matSuffix","","aria-hidden","false","aria-label","help-icon","color","primary",1,"margin-8","cursor-pointer",3,"matTooltip"],[1,"flex","flex-col","gt-sm:flex-row","gt-sm:gap-2"],[1,"mat-block","flex-1"],["type","number","step","1","min","1","max","2147483647","matInput","","formControlName","minInsideDuration","required",""],["formControlName","minInsideDurationTimeUnit","required",""],["type","number","step","1","min","1","max","2147483647","matInput","","formControlName","minOutsideDuration","required",""],["formControlName","minOutsideDurationTimeUnit","required",""]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"section",1)(2,"div",2),t.ɵɵtext(3,"tb.rulenode.coordinate-field-names"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"section")(5,"div",3)(6,"mat-form-field",4)(7,"mat-label"),t.ɵɵtext(8),t.ɵɵpipe(9,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(10,"input",5),t.ɵɵtemplate(11,rn,3,3,"mat-error",6),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"mat-form-field",4)(13,"mat-label"),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(16,"input",7),t.ɵɵtemplate(17,an,3,3,"mat-error",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(18,"div",8),t.ɵɵtext(19,"tb.rulenode.coordinate-field-hint"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(20,"section",1)(21,"div",2),t.ɵɵtext(22,"tb.rulenode.geofence-configuration"),t.ɵɵelementEnd(),t.ɵɵelementStart(23,"section",9)(24,"mat-form-field",10)(25,"mat-label"),t.ɵɵtext(26),t.ɵɵpipe(27,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(28,"mat-select",11),t.ɵɵtemplate(29,on,3,4,"mat-option",12),t.ɵɵelementEnd()(),t.ɵɵelementStart(30,"div",13),t.ɵɵpipe(31,"translate"),t.ɵɵpipe(32,"translate"),t.ɵɵelementStart(33,"mat-slide-toggle",14),t.ɵɵtext(34),t.ɵɵpipe(35,"translate"),t.ɵɵelementEnd()(),t.ɵɵtemplate(36,sn,9,7,"mat-form-field",15)(37,fn,28,17,"div",16)(38,hn,9,4,"div",16),t.ɵɵelementEnd()(),t.ɵɵelementStart(39,"section",17)(40,"div",18)(41,"div",19),t.ɵɵtext(42),t.ɵɵpipe(43,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(44,"tb-toggle-select",20),t.ɵɵtemplate(45,yn,3,4,"tb-toggle-option",12),t.ɵɵelementEnd()(),t.ɵɵelementStart(46,"div",21),t.ɵɵtext(47),t.ɵɵpipe(48,"translate"),t.ɵɵpipe(49,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(50,"section",9),t.ɵɵtemplate(51,Fn,27,8,"div",6),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.geoActionConfigForm),t.ɵɵadvance(8),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(9,18,"tb.rulenode.latitude-field-name")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.geoActionConfigForm.get("latitudeKeyName").hasError("required")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(15,20,"tb.rulenode.longitude-field-name")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.geoActionConfigForm.get("longitudeKeyName").hasError("required")),t.ɵɵadvance(3),t.ɵɵclassProp("no-padding-bottom",!n.defaultPaddingEnable),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(27,22,"tb.rulenode.perimeter-type")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.perimeterTypes),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",n.geoActionConfigForm.get("perimeterType").value===n.perimeterType.CIRCLE?t.ɵɵpipeBind2(31,24,"tb.rulenode.fetch-circle-parameter-info-from-metadata-hint",t.ɵɵpureFunction1(38,nn,n.geoActionConfigForm.get("perimeterKeyName").valid?n.geoActionConfigForm.get("perimeterKeyName").value:"ss_perimeter")):t.ɵɵpipeBind2(32,27,"tb.rulenode.fetch-poligon-parameter-info-from-metadata-hint",t.ɵɵpureFunction1(40,nn,n.geoActionConfigForm.get("perimeterKeyName").valid?n.geoActionConfigForm.get("perimeterKeyName").value:"ss_perimeter"))),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(35,30,"tb.rulenode.fetch-perimeter-info-from-metadata")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.geoActionConfigForm.get("fetchPerimeterInfoFromMessageMetadata").value),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.geoActionConfigForm.get("perimeterType").value===n.perimeterType.CIRCLE&&!n.geoActionConfigForm.get("fetchPerimeterInfoFromMessageMetadata").value),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.geoActionConfigForm.get("perimeterType").value===n.perimeterType.POLYGON&&!n.geoActionConfigForm.get("fetchPerimeterInfoFromMessageMetadata").value),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(43,32,"tb.rulenode.presence-monitoring-strategy")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.presenceMonitoringStrategyKeys),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",!1===n.geoActionConfigForm.get("reportPresenceStatusOnEachMessage").value?t.ɵɵpipeBind1(48,34,"tb.rulenode.presence-monitoring-strategy-on-first-message-hint"):t.ɵɵpipeBind1(49,36,"tb.rulenode.presence-monitoring-strategy-on-each-message-hint")," "),t.ɵɵadvance(4),t.ɵɵproperty("ngIf",!1===n.geoActionConfigForm.get("reportPresenceStatusOnEachMessage").value))},dependencies:t.ɵɵgetComponentDepsFactory(qn),styles:["[_nghost-%COMP%] .slide-toggle[_ngcontent-%COMP%]{margin-bottom:18px}"]})}}e("GpsGeoActionConfigComponent",qn);const An=["jsFuncComponent"],kn=["tbelFuncComponent"],Nn=()=>["msg","metadata","msgType"];function wn(e,n){1&e&&t.ɵɵelement(0,"tb-script-lang",8)}function Mn(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-js-func",9,0)(2,"button",10),t.ɵɵpipe(3,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.testScript())})),t.ɵɵelementStart(4,"mat-icon",11),t.ɵɵtext(5,"bug_report"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("functionArgs",t.ɵɵpureFunction0(4,Nn)),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(3,2,e.testScriptLabel))}}function Bn(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-js-func",12,1)(2,"button",10),t.ɵɵpipe(3,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.testScript())})),t.ɵɵelementStart(4,"mat-icon",11),t.ɵɵtext(5,"bug_report"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("functionArgs",t.ɵɵpureFunction0(6,Nn))("disableUndefinedCheck",!0)("scriptLanguage",e.scriptLanguage.TBEL),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(3,4,e.testScriptLabel))}}class Vn extends i{constructor(e,t,r,a){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=r,this.translate=a,this.tbelEnabled=D(this.store).tbelEnabled,this.scriptLanguage=s,this.changeScript=new n,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-to-string-function"}configForm(){return this.logConfigForm}onConfigurationSet(e){this.logConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:s.JS,[N.required]],jsScript:[e?e.jsScript:null,[]],tbelScript:[e?e.tbelScript:null,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.logConfigForm.get("scriptLang").value;t!==s.TBEL||this.tbelEnabled||(t=s.JS,this.logConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.logConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.logConfigForm.get("jsScript").setValidators(t===s.JS?[N.required]:[]),this.logConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.logConfigForm.get("tbelScript").setValidators(t===s.TBEL?[N.required]:[]),this.logConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=s.JS)),e}testScript(e){const t=this.logConfigForm.get("scriptLang").value,n=t===s.JS?"jsScript":"tbelScript",r=t===s.JS?"rulenode/log_node_script_fn":"rulenode/tbel/log_node_script_fn",a=this.logConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(a,"string",this.translate.instant("tb.rulenode.to-string"),"ToString",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.logConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.logConfigForm.get("scriptLang").value===s.JS&&this.jsFuncComponent.validateOnSubmit()}static{this.ɵfac=function(e){return new(e||Vn)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder),t.ɵɵdirectiveInject(L.NodeScriptTestService),t.ɵɵdirectiveInject(K.TranslateService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Vn,selectors:[["tb-action-node-log-config"]],viewQuery:function(e,n){if(1&e&&(t.ɵɵviewQuery(An,5),t.ɵɵviewQuery(kn,5)),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.jsFuncComponent=e.first),t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.tbelFuncComponent=e.first)}},features:[t.ɵɵInheritDefinitionFeature],decls:8,vars:7,consts:[["jsFuncComponent",""],["tbelFuncComponent",""],[1,"flex","flex-col",3,"formGroup"],["formControlName","scriptLang",4,"ngIf"],["formControlName","jsScript","functionName","ToString","helpId","rulenode/log_node_script_fn","noValidate","true",3,"functionArgs",4,"ngIf"],["formControlName","tbelScript","functionName","ToString","helpId","rulenode/tbel/log_node_script_fn","noValidate","true",3,"functionArgs","disableUndefinedCheck","scriptLanguage",4,"ngIf"],[1,"flex","flex-row"],["mat-button","","mat-raised-button","","color","primary",3,"click"],["formControlName","scriptLang"],["formControlName","jsScript","functionName","ToString","helpId","rulenode/log_node_script_fn","noValidate","true",3,"functionArgs"],["toolbarSuffixButton","","mat-icon-button","","matTooltipPosition","above",1,"tb-mat-32",3,"click","matTooltip"],["color","primary",1,"material-icons"],["formControlName","tbelScript","functionName","ToString","helpId","rulenode/tbel/log_node_script_fn","noValidate","true",3,"functionArgs","disableUndefinedCheck","scriptLanguage"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",2),t.ɵɵtemplate(1,wn,1,0,"tb-script-lang",3)(2,Mn,6,5,"tb-js-func",4)(3,Bn,6,7,"tb-js-func",5),t.ɵɵelementStart(4,"div",6)(5,"button",7),t.ɵɵlistener("click",(function(){return n.testScript()})),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.logConfigForm),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.tbelEnabled),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.logConfigForm.get("scriptLang").value===n.scriptLanguage.JS),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.logConfigForm.get("scriptLang").value===n.scriptLanguage.TBEL),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(7,5,n.testScriptLabel)," "))},dependencies:t.ɵɵgetComponentDepsFactory(Vn),encapsulation:2})}}function On(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.interval-seconds-required")," "))}function Dn(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.min-interval-seconds-message")," "))}function Ln(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.output-timeseries-key-prefix-required")," "))}e("LogConfigComponent",Vn);class Pn extends i{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.msgCountConfigForm}onConfigurationSet(e){this.msgCountConfigForm=this.fb.group({interval:[e?e.interval:null,[N.required,N.min(1)]],telemetryPrefix:[e?e.telemetryPrefix:null,[N.required]]})}static{this.ɵfac=function(e){return new(e||Pn)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Pn,selectors:[["tb-action-node-msg-count-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:12,vars:4,consts:[[1,"flex","flex-col",3,"formGroup"],[1,"mat-block"],["translate",""],["required","","type","number","min","1","step","1","matInput","","formControlName","interval"],[4,"ngIf"],["required","","matInput","","formControlName","telemetryPrefix"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"mat-form-field",1)(2,"mat-label",2),t.ɵɵtext(3,"tb.rulenode.interval-seconds"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",3),t.ɵɵtemplate(5,On,3,3,"mat-error",4)(6,Dn,3,3,"mat-error",4),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-form-field",1)(8,"mat-label",2),t.ɵɵtext(9,"tb.rulenode.output-timeseries-key-prefix"),t.ɵɵelementEnd(),t.ɵɵelement(10,"input",5),t.ɵɵtemplate(11,Ln,3,3,"mat-error",4),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.msgCountConfigForm),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.msgCountConfigForm.get("interval").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.msgCountConfigForm.get("interval").hasError("min")),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.msgCountConfigForm.get("telemetryPrefix").hasError("required")))},dependencies:t.ɵɵgetComponentDepsFactory(Pn),encapsulation:2})}}function Rn(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.period-seconds-required")," "))}function _n(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.min-period-0-seconds-message")," "))}function jn(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",5)(1,"mat-label",6),t.ɵɵtext(2,"tb.rulenode.period-seconds"),t.ɵɵelementEnd(),t.ɵɵelement(3,"input",9),t.ɵɵtemplate(4,Rn,3,3,"mat-error",8)(5,_n,3,3,"mat-error",8),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(4),t.ɵɵproperty("ngIf",e.msgDelayConfigForm.get("periodInSeconds").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.msgDelayConfigForm.get("periodInSeconds").hasError("min"))}}function Gn(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.period-in-seconds-pattern-required")," "))}function Kn(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",10)(1,"mat-label",6),t.ɵɵtext(2,"tb.rulenode.period-in-seconds-pattern"),t.ɵɵelementEnd(),t.ɵɵelement(3,"input",11),t.ɵɵtemplate(4,Gn,3,3,"mat-error",8),t.ɵɵelementStart(5,"mat-hint",6),t.ɵɵtext(6,"tb.rulenode.general-pattern-hint"),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(4),t.ɵɵproperty("ngIf",e.msgDelayConfigForm.get("periodInSecondsPattern").hasError("required"))}}function Un(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.max-pending-messages-required")," "))}function Hn(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.max-pending-messages-range")," "))}function zn(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.max-pending-messages-range")," "))}e("MsgCountConfigComponent",Pn);class $n extends i{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.msgDelayConfigForm}onConfigurationSet(e){this.msgDelayConfigForm=this.fb.group({useMetadataPeriodInSecondsPatterns:[!!e&&e.useMetadataPeriodInSecondsPatterns,[]],periodInSeconds:[e?e.periodInSeconds:null,[]],periodInSecondsPattern:[e?e.periodInSecondsPattern:null,[]],maxPendingMsgs:[e?e.maxPendingMsgs:null,[N.required,N.min(1),N.max(1e5)]]})}validatorTriggers(){return["useMetadataPeriodInSecondsPatterns"]}updateValidators(e){this.msgDelayConfigForm.get("useMetadataPeriodInSecondsPatterns").value?(this.msgDelayConfigForm.get("periodInSecondsPattern").setValidators([N.required]),this.msgDelayConfigForm.get("periodInSeconds").setValidators([])):(this.msgDelayConfigForm.get("periodInSecondsPattern").setValidators([]),this.msgDelayConfigForm.get("periodInSeconds").setValidators([N.required,N.min(0)])),this.msgDelayConfigForm.get("periodInSecondsPattern").updateValueAndValidity({emitEvent:e}),this.msgDelayConfigForm.get("periodInSeconds").updateValueAndValidity({emitEvent:e})}static{this.ɵfac=function(e){return new(e||$n)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:$n,selectors:[["tb-action-node-msg-delay-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:16,vars:9,consts:[["periodInSecondsPattern",""],[1,"flex","flex-col",3,"formGroup"],["formControlName","useMetadataPeriodInSecondsPatterns"],["translate","",1,"tb-hint"],["class","mat-block",4,"ngIf","ngIfElse"],[1,"mat-block"],["translate",""],["required","","type","number","min","1","max","100000","step","1","matInput","","formControlName","maxPendingMsgs"],[4,"ngIf"],["required","","type","number","min","0","step","1","matInput","","formControlName","periodInSeconds"],["subscriptSizing","dynamic",1,"mat-block"],["required","","matInput","","formControlName","periodInSecondsPattern"]],template:function(e,n){if(1&e&&(t.ɵɵelementStart(0,"section",1)(1,"mat-checkbox",2),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",3),t.ɵɵtext(5,"tb.rulenode.use-metadata-period-in-seconds-patterns-hint"),t.ɵɵelementEnd(),t.ɵɵtemplate(6,jn,6,2,"mat-form-field",4)(7,Kn,7,1,"ng-template",null,0,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(9,"mat-form-field",5)(10,"mat-label",6),t.ɵɵtext(11,"tb.rulenode.max-pending-messages"),t.ɵɵelementEnd(),t.ɵɵelement(12,"input",7),t.ɵɵtemplate(13,Un,3,3,"mat-error",8)(14,Hn,3,3,"mat-error",8)(15,zn,3,3,"mat-error",8),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵreference(8);t.ɵɵproperty("formGroup",n.msgDelayConfigForm),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,7,"tb.rulenode.use-metadata-period-in-seconds-patterns")," "),t.ɵɵadvance(4),t.ɵɵproperty("ngIf",!0!==n.msgDelayConfigForm.get("useMetadataPeriodInSecondsPatterns").value)("ngIfElse",e),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",n.msgDelayConfigForm.get("maxPendingMsgs").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.msgDelayConfigForm.get("maxPendingMsgs").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.msgDelayConfigForm.get("maxPendingMsgs").hasError("max"))}},dependencies:t.ɵɵgetComponentDepsFactory($n),encapsulation:2})}}e("MsgDelayConfigComponent",$n);const Qn=()=>({standalone:!0});function Jn(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",10),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.telemetryTypeTranslationsMap.get(e))," ")}}class Yn extends i{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopes=Object.keys(o),this.telemetryTypeTranslationsMap=l}configForm(){return this.pushToCloudConfigForm}onConfigurationSet(e){this.pushToCloudConfigForm=this.fb.group({scope:[e?e.scope:null,[N.required]]})}static{this.ɵfac=function(e){return new(e||Yn)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Yn,selectors:[["tb-action-node-push-to-cloud-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:19,vars:16,consts:[[1,"tb-form-panel","no-border","no-padding",3,"formGroup"],[1,"tb-form-panel","stroked"],[3,"hintText"],[1,"tb-form-row","no-border","no-padding","tb-standard-fields"],[1,"flex"],["required","","matInput","","formControlName","scope",1,"tb-entity-type-select"],[3,"value",4,"ngFor","ngForOf"],["type","text","matInput","","readonly","","disabled","",3,"ngModel","ngModelOptions"],["type","button","matSuffix","","mat-icon-button","","aria-label","Copy","ngxClipboard","",3,"cbContent","matTooltip"],["aria-hidden","false","aria-label","help-icon"],[3,"value"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1),t.ɵɵelement(2,"tb-example-hint",2),t.ɵɵelementStart(3,"div",3)(4,"mat-form-field",4)(5,"mat-label"),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(8,"mat-select",5),t.ɵɵtemplate(9,Jn,3,4,"mat-option",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"mat-form-field",4)(11,"mat-label"),t.ɵɵtext(12),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(14,"input",7),t.ɵɵelementStart(15,"button",8),t.ɵɵpipe(16,"translate"),t.ɵɵelementStart(17,"mat-icon",9),t.ɵɵtext(18,"content_copy "),t.ɵɵelementEnd()()()()()()),2&e&&(t.ɵɵproperty("formGroup",n.pushToCloudConfigForm),t.ɵɵadvance(2),t.ɵɵproperty("hintText","tb.rulenode.attributes-scope-hint"),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(7,9,"tb.rulenode.attributes-scope")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.attributeScopes),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(13,11,"tb.rulenode.attributes-scope-value")),t.ɵɵadvance(2),t.ɵɵproperty("ngModel",n.pushToCloudConfigForm.get("scope").value)("ngModelOptions",t.ɵɵpureFunction0(15,Qn)),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(16,13,"tb.rulenode.attributes-scope-value-copy")),t.ɵɵproperty("cbContent",n.pushToCloudConfigForm.get("scope").value))},dependencies:t.ɵɵgetComponentDepsFactory(Yn),encapsulation:2})}}e("PushToCloudConfigComponent",Yn);const Wn=()=>({standalone:!0});function Xn(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",10),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.telemetryTypeTranslationsMap.get(e))," ")}}class Zn extends i{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopes=Object.keys(o),this.telemetryTypeTranslationsMap=l}configForm(){return this.pushToEdgeConfigForm}onConfigurationSet(e){this.pushToEdgeConfigForm=this.fb.group({scope:[e?e.scope:null,[N.required]]})}static{this.ɵfac=function(e){return new(e||Zn)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Zn,selectors:[["tb-action-node-push-to-edge-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:19,vars:16,consts:[[1,"tb-form-panel","no-border","no-padding",3,"formGroup"],[1,"tb-form-panel","stroked"],[3,"hintText"],[1,"tb-form-row","no-border","no-padding","tb-standard-fields"],[1,"flex"],["required","","matInput","","formControlName","scope",1,"tb-entity-type-select"],[3,"value",4,"ngFor","ngForOf"],["type","text","matInput","","readonly","","disabled","",3,"ngModel","ngModelOptions"],["type","button","matSuffix","","mat-icon-button","","aria-label","Copy","ngxClipboard","",3,"cbContent","matTooltip"],["aria-hidden","false","aria-label","help-icon"],[3,"value"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1),t.ɵɵelement(2,"tb-example-hint",2),t.ɵɵelementStart(3,"div",3)(4,"mat-form-field",4)(5,"mat-label"),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(8,"mat-select",5),t.ɵɵtemplate(9,Xn,3,4,"mat-option",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"mat-form-field",4)(11,"mat-label"),t.ɵɵtext(12),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(14,"input",7),t.ɵɵelementStart(15,"button",8),t.ɵɵpipe(16,"translate"),t.ɵɵelementStart(17,"mat-icon",9),t.ɵɵtext(18,"content_copy "),t.ɵɵelementEnd()()()()()()),2&e&&(t.ɵɵproperty("formGroup",n.pushToEdgeConfigForm),t.ɵɵadvance(2),t.ɵɵproperty("hintText","tb.rulenode.attributes-scope-hint"),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(7,9,"tb.rulenode.attributes-scope")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.attributeScopes),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(13,11,"tb.rulenode.attributes-scope-value")),t.ɵɵadvance(2),t.ɵɵproperty("ngModel",n.pushToEdgeConfigForm.get("scope").value)("ngModelOptions",t.ɵɵpureFunction0(15,Wn)),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(16,13,"tb.rulenode.attributes-scope-value-copy")),t.ɵɵproperty("cbContent",n.pushToEdgeConfigForm.get("scope").value))},dependencies:t.ɵɵgetComponentDepsFactory(Zn),encapsulation:2})}}e("PushToEdgeConfigComponent",Zn);class er extends i{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.rpcReplyConfigForm}onConfigurationSet(e){this.rpcReplyConfigForm=this.fb.group({serviceIdMetaDataAttribute:[e?e.serviceIdMetaDataAttribute:null,[]],sessionIdMetaDataAttribute:[e?e.sessionIdMetaDataAttribute:null,[]],requestIdMetaDataAttribute:[e?e.requestIdMetaDataAttribute:null,[]]})}static{this.ɵfac=function(e){return new(e||er)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:er,selectors:[["tb-action-node-rpc-reply-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:17,vars:2,consts:[[1,"tb-form-panel","stroked","no-padding-bottom",3,"formGroup"],["translate","",1,"tb-form-panel-title"],[3,"hintText"],[1,"tb-form-row","no-border","no-padding","tb-standard-fields","column-xs"],[1,"flex"],["translate",""],["matInput","","formControlName","serviceIdMetaDataAttribute"],["matInput","","formControlName","sessionIdMetaDataAttribute"],["matInput","","formControlName","requestIdMetaDataAttribute"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1),t.ɵɵtext(2,"tb.rulenode.reply-routing-configuration"),t.ɵɵelementEnd(),t.ɵɵelement(3,"tb-example-hint",2),t.ɵɵelementStart(4,"div",3)(5,"mat-form-field",4)(6,"mat-label",5),t.ɵɵtext(7,"tb.rulenode.service-id-metadata-attribute"),t.ɵɵelementEnd(),t.ɵɵelement(8,"input",6),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"mat-form-field",4)(10,"mat-label",5),t.ɵɵtext(11,"tb.rulenode.session-id-metadata-attribute"),t.ɵɵelementEnd(),t.ɵɵelement(12,"input",7),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"mat-form-field",4)(14,"mat-label",5),t.ɵɵtext(15,"tb.rulenode.request-id-metadata-attribute"),t.ɵɵelementEnd(),t.ɵɵelement(16,"input",8),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.rpcReplyConfigForm),t.ɵɵadvance(3),t.ɵɵproperty("hintText","tb.rulenode.rpc-reply-routing-configuration-hint"))},dependencies:t.ɵɵgetComponentDepsFactory(er),encapsulation:2})}}function tr(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.timeout-required")," "))}function nr(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.min-timeout-message")," "))}e("RpcReplyConfigComponent",er);class rr extends i{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.rpcRequestConfigForm}onConfigurationSet(e){this.rpcRequestConfigForm=this.fb.group({timeoutInSeconds:[e?e.timeoutInSeconds:null,[N.required,N.min(0)]]})}static{this.ɵfac=function(e){return new(e||rr)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:rr,selectors:[["tb-action-node-rpc-request-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:7,vars:3,consts:[[1,"flex","flex-col",3,"formGroup"],[1,"mat-block","flex-1"],["translate",""],["type","number","min","0","step","1","matInput","","formControlName","timeoutInSeconds","required",""],[4,"ngIf"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"mat-form-field",1)(2,"mat-label",2),t.ɵɵtext(3,"tb.rulenode.timeout-sec"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",3),t.ɵɵtemplate(5,tr,3,3,"mat-error",4)(6,nr,3,3,"mat-error",4),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.rpcRequestConfigForm),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.rpcRequestConfigForm.get("timeoutInSeconds").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.rpcRequestConfigForm.get("timeoutInSeconds").hasError("min")))},dependencies:t.ɵɵgetComponentDepsFactory(rr),encapsulation:2})}}function ar(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.custom-table-name-required")," "))}function ir(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.min-default-ttl-message")," "))}function or(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.default-ttl-required")," "))}e("RpcRequestConfigComponent",rr);class lr extends i{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.saveToCustomTableConfigForm}onConfigurationSet(e){this.saveToCustomTableConfigForm=this.fb.group({tableName:[e?e.tableName:null,[N.required,N.pattern(/.*\S.*/)]],fieldsMapping:[e?e.fieldsMapping:null,[N.required]],defaultTtl:[e?e.defaultTtl:0,[N.required,N.min(0)]]})}prepareOutputConfig(e){return e.tableName=e.tableName.trim(),e}static{this.ɵfac=function(e){return new(e||lr)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:lr,selectors:[["tb-action-node-custom-table-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:24,vars:26,consts:[[1,"tb-form-panel","no-border","no-padding",3,"formGroup"],["subscriptSizing","dynamic",1,"mat-block"],["translate",""],["required","","matInput","","formControlName","tableName"],["aria-hidden","false","aria-label","help-icon","matSuffix","",1,"help-icon","margin-8","cursor-pointer",3,"matTooltip"],[4,"ngIf"],["required","","formControlName","fieldsMapping",3,"labelText","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText"],[1,"mat-block","flex-1"],["type","number","min","0","step","1","matInput","","formControlName","defaultTtl","required",""]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"mat-form-field",1)(2,"mat-label",2),t.ɵɵtext(3,"tb.rulenode.custom-table-name"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",3),t.ɵɵelementStart(5,"mat-icon",4),t.ɵɵpipe(6,"translate"),t.ɵɵtext(7," help "),t.ɵɵelementEnd(),t.ɵɵtemplate(8,ar,3,3,"mat-error",5),t.ɵɵelementEnd(),t.ɵɵelement(9,"tb-kv-map-config",6),t.ɵɵpipe(10,"translate"),t.ɵɵpipe(11,"translate"),t.ɵɵpipe(12,"translate"),t.ɵɵpipe(13,"translate"),t.ɵɵpipe(14,"translate"),t.ɵɵpipe(15,"translate"),t.ɵɵelementStart(16,"mat-form-field",7)(17,"mat-label",2),t.ɵɵtext(18,"tb.rulenode.default-ttl"),t.ɵɵelementEnd(),t.ɵɵelement(19,"input",8),t.ɵɵelementStart(20,"mat-hint",2),t.ɵɵtext(21,"tb.rulenode.default-ttl-zero-hint"),t.ɵɵelementEnd(),t.ɵɵtemplate(22,ir,3,3,"mat-error",5)(23,or,3,3,"mat-error",5),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.saveToCustomTableConfigForm),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(6,12,"tb.rulenode.custom-table-hint")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.saveToCustomTableConfigForm.get("tableName").hasError("required")||n.saveToCustomTableConfigForm.get("tableName").hasError("pattern")),t.ɵɵadvance(),t.ɵɵproperty("labelText",t.ɵɵpipeBind1(10,14,"tb.rulenode.fields-mapping"))("requiredText",t.ɵɵpipeBind1(11,16,"tb.rulenode.fields-mapping-required"))("keyText",t.ɵɵpipeBind1(12,18,"tb.rulenode.message-field"))("keyRequiredText",t.ɵɵpipeBind1(13,20,"tb.rulenode.message-field-required"))("valText",t.ɵɵpipeBind1(14,22,"tb.rulenode.table-col"))("valRequiredText",t.ɵɵpipeBind1(15,24,"tb.rulenode.table-col-required"))("hintText","tb.rulenode.fields-mapping-hint"),t.ɵɵadvance(13),t.ɵɵproperty("ngIf",n.saveToCustomTableConfigForm.get("defaultTtl").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.saveToCustomTableConfigForm.get("defaultTtl").hasError("required")))},dependencies:t.ɵɵgetComponentDepsFactory(lr),encapsulation:2})}}function sr(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.default-ttl-required")," "))}function pr(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.min-default-ttl-message")," "))}e("SaveToCustomTableConfigComponent",lr);class mr extends i{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.timeseriesConfigForm}onConfigurationSet(e){this.timeseriesConfigForm=this.fb.group({defaultTTL:[e?e.defaultTTL:null,[N.required,N.min(0)]],skipLatestPersistence:[!!e&&e.skipLatestPersistence,[]],useServerTs:[!!e&&e.useServerTs,[]]})}static{this.ɵfac=function(e){return new(e||mr)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:mr,selectors:[["tb-action-node-timeseries-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:21,vars:18,consts:[[1,"tb-form-panel","no-border","no-padding",3,"formGroup"],[1,"mat-block","flex-1"],["translate",""],["type","number","min","0","step","1","matInput","","formControlName","defaultTTL","required",""],["aria-hidden","false","aria-label","help-icon","matSuffix","",1,"help-icon","margin-8","cursor-pointer",3,"matTooltip"],[4,"ngIf"],[1,"tb-form-panel","stroked"],[1,"tb-form-row","no-border","no-padding",3,"tb-hint-tooltip-icon"],["formControlName","useServerTs",1,"mat-slide"],["formControlName","skipLatestPersistence",1,"mat-slide"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"mat-form-field",1)(2,"mat-label",2),t.ɵɵtext(3,"tb.rulenode.default-ttl"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",3),t.ɵɵelementStart(5,"mat-icon",4),t.ɵɵpipe(6,"translate"),t.ɵɵtext(7," help "),t.ɵɵelementEnd(),t.ɵɵtemplate(8,sr,3,3,"mat-error",5)(9,pr,3,3,"mat-error",5),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"div",6)(11,"div",7),t.ɵɵpipe(12,"translate"),t.ɵɵelementStart(13,"mat-slide-toggle",8),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(16,"div",7),t.ɵɵpipe(17,"translate"),t.ɵɵelementStart(18,"mat-slide-toggle",9),t.ɵɵtext(19),t.ɵɵpipe(20,"translate"),t.ɵɵelementEnd()()()()),2&e&&(t.ɵɵproperty("formGroup",n.timeseriesConfigForm),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(6,8,"tb.rulenode.default-ttl-hint")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.timeseriesConfigForm.get("defaultTTL").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.timeseriesConfigForm.get("defaultTTL").hasError("min")),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(12,10,"tb.rulenode.use-server-ts-hint")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(15,12,"tb.rulenode.use-server-ts")," "),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(17,14,"tb.rulenode.skip-latest-persistence-hint")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(20,16,"tb.rulenode.skip-latest-persistence")," "))},dependencies:t.ɵɵgetComponentDepsFactory(mr),encapsulation:2})}}function dr(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.customer-name-pattern-required")," "))}function ur(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",6)(1,"mat-label",7),t.ɵɵtext(2,"tb.rulenode.customer-name-pattern"),t.ɵɵelementEnd(),t.ɵɵelement(3,"input",8),t.ɵɵtemplate(4,dr,3,3,"mat-error",9),t.ɵɵelementStart(5,"mat-hint",7),t.ɵɵtext(6,"tb.rulenode.customer-name-pattern-hint"),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(4),t.ɵɵproperty("ngIf",e.unassignCustomerConfigForm.get("customerNamePattern").hasError("required")||e.unassignCustomerConfigForm.get("customerNamePattern").hasError("pattern"))}}e("TimeseriesConfigComponent",mr);class cr extends i{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.unassignCustomerConfigForm}prepareInputConfig(e){return{customerNamePattern:P(e?.customerNamePattern)?e.customerNamePattern:null,unassignFromCustomer:P(e?.customerNamePattern)}}onConfigurationSet(e){this.unassignCustomerConfigForm=this.fb.group({customerNamePattern:[e.customerNamePattern,[]],unassignFromCustomer:[e.unassignFromCustomer,[]]})}validatorTriggers(){return["unassignFromCustomer"]}updateValidators(e){this.unassignCustomerConfigForm.get("unassignFromCustomer").value?this.unassignCustomerConfigForm.get("customerNamePattern").setValidators([N.required,N.pattern(/.*\S.*/)]):this.unassignCustomerConfigForm.get("customerNamePattern").setValidators([]),this.unassignCustomerConfigForm.get("customerNamePattern").updateValueAndValidity({emitEvent:e})}prepareOutputConfig(e){return{customerNamePattern:e.unassignFromCustomer?e.customerNamePattern.trim():null}}static{this.ɵfac=function(e){return new(e||cr)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:cr,selectors:[["tb-action-node-un-assign-to-customer-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:9,vars:10,consts:[[1,"flex","flex-col",3,"formGroup"],[1,"tb-form-panel","no-padding","no-border"],[1,"tb-form-panel","stroked"],[1,"tb-form-row","no-border","no-padding",3,"tb-hint-tooltip-icon"],["formControlName","unassignFromCustomer",1,"mat-slide"],["class","mat-block","subscriptSizing","dynamic",4,"ngIf"],["subscriptSizing","dynamic",1,"mat-block"],["translate",""],["required","","matInput","","formControlName","customerNamePattern"],[4,"ngIf"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1)(2,"div",2)(3,"div",3),t.ɵɵpipe(4,"translate"),t.ɵɵelementStart(5,"mat-slide-toggle",4),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd()(),t.ɵɵtemplate(8,ur,7,1,"mat-form-field",5),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.unassignCustomerConfigForm),t.ɵɵadvance(2),t.ɵɵclassProp("no-padding-bottom",n.unassignCustomerConfigForm.get("unassignFromCustomer").value),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(4,6,"tb.rulenode.unassign-from-customer-tooltip")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(7,8,"tb.rulenode.unassign-from-customer")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.unassignCustomerConfigForm.get("unassignFromCustomer").value))},dependencies:t.ɵɵgetComponentDepsFactory(cr),encapsulation:2})}}e("UnassignCustomerConfigComponent",cr);class fr extends i{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.sendRestApiCallReplyConfigForm}onConfigurationSet(e){this.sendRestApiCallReplyConfigForm=this.fb.group({requestIdMetaDataAttribute:[e?e.requestIdMetaDataAttribute:null,[]],serviceIdMetaDataAttribute:[e?e.serviceIdMetaDataAttribute:null,[]]})}static{this.ɵfac=function(e){return new(e||fr)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:fr,selectors:[["tb-action-node-send-rest-api-call-reply-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:13,vars:2,consts:[[1,"tb-form-panel","stroked","no-padding-bottom",3,"formGroup"],["translate","",1,"tb-form-panel-title"],[3,"hintText"],[1,"tb-form-row","no-border","no-padding","tb-standard-fields","column-xs"],[1,"flex"],["translate",""],["matInput","","formControlName","serviceIdMetaDataAttribute"],["matInput","","formControlName","requestIdMetaDataAttribute"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1),t.ɵɵtext(2,"tb.rulenode.reply-routing-configuration"),t.ɵɵelementEnd(),t.ɵɵelement(3,"tb-example-hint",2),t.ɵɵelementStart(4,"div",3)(5,"mat-form-field",4)(6,"mat-label",5),t.ɵɵtext(7,"tb.rulenode.service-id-metadata-attribute"),t.ɵɵelementEnd(),t.ɵɵelement(8,"input",6),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"mat-form-field",4)(10,"mat-label",5),t.ɵɵtext(11,"tb.rulenode.request-id-metadata-attribute"),t.ɵɵelementEnd(),t.ɵɵelement(12,"input",7),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.sendRestApiCallReplyConfigForm),t.ɵɵadvance(3),t.ɵɵproperty("hintText","tb.rulenode.reply-routing-configuration-hint"))},dependencies:t.ɵɵgetComponentDepsFactory(fr),encapsulation:2})}}e("SendRestApiCallReplyConfigComponent",fr);const gr=["attributeChipList"],hr=()=>({standalone:!0});function yr(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",21),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.telemetryTypeTranslationsMap.get(e))," ")}}function br(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-chip-row",22),t.ɵɵlistener("removed",(function(){const n=t.ɵɵrestoreView(e).$implicit,r=t.ɵɵnextContext();return t.ɵɵresetView(r.removeKey(n))})),t.ɵɵtext(1),t.ɵɵelementStart(2,"mat-icon",23),t.ɵɵtext(3,"close"),t.ɵɵelementEnd()()}if(2&e){const e=n.$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function vr(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"tb.rulenode.attributes-keys-required")))}function xr(e,n){1&e&&(t.ɵɵelementStart(0,"div",18),t.ɵɵpipe(1,"translate"),t.ɵɵelementStart(2,"mat-slide-toggle",24),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(1,2,"tb.rulenode.notify-device-on-delete-hint")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,4,"tb.rulenode.notify-device")," "))}class Cr extends i{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopeMap=o,this.attributeScopes=Object.keys(o),this.telemetryTypeTranslationsMap=l,this.separatorKeysCodes=[U,H,z]}configForm(){return this.deleteAttributesConfigForm}onConfigurationSet(e){this.deleteAttributesConfigForm=this.fb.group({scope:[e?e.scope:null,[N.required]],keys:[e?e.keys:null,[N.required]],sendAttributesDeletedNotification:[!!e&&e.sendAttributesDeletedNotification,[]],notifyDevice:[!!e&&e.notifyDevice,[]]}),this.deleteAttributesConfigForm.get("scope").valueChanges.subscribe((e=>{e!==o.SHARED_SCOPE&&this.deleteAttributesConfigForm.get("notifyDevice").patchValue(!1,{emitEvent:!1})}))}removeKey(e){const t=this.deleteAttributesConfigForm.get("keys").value,n=t.indexOf(e);n>=0&&(t.splice(n,1),this.deleteAttributesConfigForm.get("keys").patchValue(t,{emitEvent:!0}))}addKey(e){const t=e.input;let n=e.value;if((n||"").trim()){n=n.trim();let e=this.deleteAttributesConfigForm.get("keys").value;e&&-1!==e.indexOf(n)||(e||(e=[]),e.push(n),this.deleteAttributesConfigForm.get("keys").patchValue(e,{emitEvent:!0}))}t&&(t.value="")}static{this.ɵfac=function(e){return new(e||Cr)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Cr,selectors:[["tb-action-node-delete-attributes-config"]],viewQuery:function(e,n){if(1&e&&t.ɵɵviewQuery(gr,5),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.attributeChipList=e.first)}},features:[t.ɵɵInheritDefinitionFeature],decls:41,vars:31,consts:[["attributeChipList",""],[1,"tb-form-panel","no-border","no-padding",3,"formGroup"],[1,"tb-form-panel","stroked"],[3,"hintText"],[1,"tb-form-row","no-border","no-padding","tb-standard-fields"],[1,"flex"],["required","","matInput","","formControlName","scope",1,"tb-entity-type-select"],[3,"value",4,"ngFor","ngForOf"],["type","text","matInput","","readonly","","disabled","",3,"ngModel","ngModelOptions"],["type","button","matSuffix","","mat-icon-button","","aria-label","Copy","ngxClipboard","",3,"cbContent","matTooltip"],["aria-hidden","false","aria-label","help-icon"],["subscriptSizing","dynamic",1,"mat-block"],["formControlName","keys"],[3,"removed",4,"ngFor","ngForOf"],["matInput","","type","text",3,"matChipInputTokenEnd","matChipInputFor","matChipInputSeparatorKeyCodes","matChipInputAddOnBlur"],[4,"ngIf"],["translate",""],[1,"tb-settings"],[1,"tb-form-row","no-border","no-padding",3,"tb-hint-tooltip-icon"],["formControlName","sendAttributesDeletedNotification",1,"mat-slide"],["class","tb-form-row no-border no-padding",3,"tb-hint-tooltip-icon",4,"ngIf"],[3,"value"],[3,"removed"],["matChipRemove",""],["formControlName","notifyDevice",1,"mat-slide"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"section",1)(1,"div",2),t.ɵɵelement(2,"tb-example-hint",3),t.ɵɵelementStart(3,"div",4)(4,"mat-form-field",5)(5,"mat-label"),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(8,"mat-select",6),t.ɵɵtemplate(9,yr,3,4,"mat-option",7),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"mat-form-field",5)(11,"mat-label"),t.ɵɵtext(12),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(14,"input",8),t.ɵɵelementStart(15,"button",9),t.ɵɵpipe(16,"translate"),t.ɵɵelementStart(17,"mat-icon",10),t.ɵɵtext(18,"content_copy "),t.ɵɵelementEnd()()()()(),t.ɵɵelementStart(19,"mat-form-field",11)(20,"mat-label"),t.ɵɵtext(21),t.ɵɵpipe(22,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(23,"mat-chip-grid",12,0),t.ɵɵtemplate(25,br,4,1,"mat-chip-row",13),t.ɵɵelementStart(26,"input",14),t.ɵɵlistener("matChipInputTokenEnd",(function(r){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.addKey(r))})),t.ɵɵelementEnd()(),t.ɵɵtemplate(27,vr,3,3,"mat-error",15),t.ɵɵelementStart(28,"mat-hint",16),t.ɵɵtext(29,"tb.rulenode.general-pattern-hint"),t.ɵɵelementEnd()(),t.ɵɵelementStart(30,"section",2)(31,"mat-expansion-panel",17)(32,"mat-expansion-panel-header")(33,"mat-panel-title",16),t.ɵɵtext(34,"tb.rulenode.advanced-settings"),t.ɵɵelementEnd()(),t.ɵɵelementStart(35,"div",18),t.ɵɵpipe(36,"translate"),t.ɵɵelementStart(37,"mat-slide-toggle",19),t.ɵɵtext(38),t.ɵɵpipe(39,"translate"),t.ɵɵelementEnd()(),t.ɵɵtemplate(40,xr,5,6,"div",20),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵreference(24);t.ɵɵproperty("formGroup",n.deleteAttributesConfigForm),t.ɵɵadvance(2),t.ɵɵproperty("hintText","tb.rulenode.attributes-scope-hint"),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(7,18,"tb.rulenode.attributes-scope")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.attributeScopes),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(13,20,"tb.rulenode.attributes-scope-value")),t.ɵɵadvance(2),t.ɵɵproperty("ngModel",n.deleteAttributesConfigForm.get("scope").value)("ngModelOptions",t.ɵɵpureFunction0(30,hr)),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(16,22,"tb.rulenode.attributes-scope-value-copy")),t.ɵɵproperty("cbContent",n.deleteAttributesConfigForm.get("scope").value),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(22,24,"tb.rulenode.attributes-keys")),t.ɵɵadvance(4),t.ɵɵproperty("ngForOf",n.deleteAttributesConfigForm.get("keys").value),t.ɵɵadvance(),t.ɵɵproperty("matChipInputFor",e)("matChipInputSeparatorKeyCodes",n.separatorKeysCodes)("matChipInputAddOnBlur",!0),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.deleteAttributesConfigForm.get("keys").hasError("required")),t.ɵɵadvance(8),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(36,26,"tb.rulenode.send-attributes-deleted-notification-hint")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(39,28,"tb.rulenode.send-attributes-deleted-notification")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.deleteAttributesConfigForm.get("scope").value===n.attributeScopeMap.SHARED_SCOPE)}},dependencies:t.ɵɵgetComponentDepsFactory(Cr),encapsulation:2})}}e("DeleteAttributesConfigComponent",Cr);const Sr=(e,t)=>[e,t];function Tr(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",8),t.ɵɵtext(1," tb.rulenode.custom-expression-field-input-required "),t.ɵɵelementEnd())}function Ir(e,n){if(1&e&&(t.ɵɵelementStart(0,"fieldset",2)(1,"legend",21),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-form-field",22),t.ɵɵelement(5,"input",23),t.ɵɵtemplate(6,Tr,2,0,"mat-error",11),t.ɵɵelementStart(7,"mat-hint",8),t.ɵɵtext(8,"tb.rulenode.custom-expression-field-input-hint"),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵtextInterpolate1("",t.ɵɵpipeBind1(3,2,"tb.rulenode.custom-expression-field-input")," *"),t.ɵɵadvance(4),t.ɵɵproperty("ngIf",e.mathFunctionConfigForm.get("customFunction").hasError("required"))}}function Er(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",24),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementStart(3,"small",25),t.ɵɵtext(4),t.ɵɵelementEnd()()),2&e){const e=n.$implicit,r=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,3,r.argumentTypeResultMap.get(e).name)," "),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",r.argumentTypeResultMap.get(e).description," ")}}function Fr(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",8),t.ɵɵtext(1," tb.rulenode.type-field-input-required "),t.ɵɵelementEnd())}function qr(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",28),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.attributeScopeMap.get(e))," ")}}function Ar(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",7)(1,"mat-label",8),t.ɵɵtext(2,"tb.rulenode.attribute-scope-field-input"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-select",26),t.ɵɵtemplate(4,qr,3,4,"mat-option",27),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(4),t.ɵɵproperty("ngForOf",e.attributeScopeResult)}}function kr(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",8),t.ɵɵtext(1," tb.rulenode.key-field-input-required "),t.ɵɵelementEnd())}function Nr(e,n){1&e&&(t.ɵɵelementStart(0,"div",29)(1,"mat-checkbox",30),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-checkbox",31),t.ɵɵtext(5),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,2,"tb.rulenode.add-to-message-field-input")," "),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(6,4,"tb.rulenode.add-to-metadata-field-input")," "))}class wr extends i{constructor(e,t){super(e),this.store=e,this.fb=t,this.MathFunction=Rt,this.ArgumentTypeResult=Gt,this.argumentTypeResultMap=Qt,this.attributeScopeMap=Xt,this.argumentsResult=Object.values(Gt),this.attributeScopeResult=Object.values(Wt)}configForm(){return this.mathFunctionConfigForm}onConfigurationSet(e){this.mathFunctionConfigForm=this.fb.group({operation:[e?e.operation:null,[N.required]],arguments:[e?e.arguments:null,[N.required]],customFunction:[e?e.customFunction:"",[N.required]],result:this.fb.group({type:[e?e.result.type:null,[N.required]],attributeScope:[e?e.result.attributeScope:null,[N.required]],key:[e?e.result.key:"",[N.required]],resultValuePrecision:[e?e.result.resultValuePrecision:0],addToBody:[!!e&&e.result.addToBody],addToMetadata:[!!e&&e.result.addToMetadata]})})}updateValidators(e){const t=this.mathFunctionConfigForm.get("operation").value,n=this.mathFunctionConfigForm.get("result.type").value;t===Rt.CUSTOM?(this.mathFunctionConfigForm.get("customFunction").enable({emitEvent:!1}),null===this.mathFunctionConfigForm.get("customFunction").value&&this.mathFunctionConfigForm.get("customFunction").patchValue("(x - 32) / 1.8",{emitEvent:!1})):this.mathFunctionConfigForm.get("customFunction").disable({emitEvent:!1}),n===Gt.ATTRIBUTE?this.mathFunctionConfigForm.get("result.attributeScope").enable({emitEvent:!1}):this.mathFunctionConfigForm.get("result.attributeScope").disable({emitEvent:!1}),this.mathFunctionConfigForm.get("customFunction").updateValueAndValidity({emitEvent:e}),this.mathFunctionConfigForm.get("result.attributeScope").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["operation","result.type"]}static{this.ɵfac=function(e){return new(e||wr)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:wr,selectors:[["tb-action-node-math-function-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:39,vars:23,consts:[[1,"flex","flex-col",3,"formGroup"],["required","","formControlName","operation",1,"flex-full","max-h-30%","xs:max-h-full","md:max-h-full"],[1,"fields-group","flex","flex-col","gap-2"],["translate","",1,"group-title"],["formControlName","arguments",3,"function"],["class","fields-group flex flex-col gap-2",4,"ngIf"],["formGroupName","result"],[1,"mat-block","flex-1"],["translate",""],["formControlName","type","required",""],["style","border-bottom: 1px solid #eee;",3,"value",4,"ngFor","ngForOf"],["translate","",4,"ngIf"],[1,"xs:flex-col","gt-xs:gap-4","flex","flex-1","flex-row"],["class","mat-block flex-1",4,"ngIf"],["floatLabel","always",1,"mat-block","flex-1"],["matInput","","formControlName","key","required",""],["aria-hidden","false","aria-label","help-icon","matSuffix","","color","primary",1,"help-icon","margin-8","cursor-pointer",3,"matTooltip"],["floatLabel","always","subscriptSizing","dynamic",1,"mat-block","flex-1"],["formControlName","resultValuePrecision","matInput","","step","1","min","0","type","number"],[3,"innerHTML"],["class","xs:flex-col gt-xs:gap-4 flex flex-1 flex-row items-stretch justify-start","style","padding-top: 16px;",4,"ngIf"],[1,"group-title"],["subscriptSizing","dynamic",1,"mat-block","no-margin-top","flex-1"],["matInput","","formControlName","customFunction","required",""],[2,"border-bottom","1px solid #eee",3,"value"],[2,"display","block","overflow","hidden","text-overflow","ellipsis","white-space","nowrap"],["required","","formControlName","attributeScope"],[3,"value",4,"ngFor","ngForOf"],[3,"value"],[1,"xs:flex-col","gt-xs:gap-4","flex","flex-1","flex-row","items-stretch","justify-start",2,"padding-top","16px"],["formControlName","addToBody"],["formControlName","addToMetadata"]],template:function(e,n){if(1&e&&(t.ɵɵelementStart(0,"section",0),t.ɵɵelement(1,"tb-math-function-autocomplete",1),t.ɵɵelementStart(2,"fieldset",2)(3,"legend",3),t.ɵɵtext(4,"tb.rulenode.argument-tile"),t.ɵɵelementEnd(),t.ɵɵelement(5,"tb-arguments-map-config",4),t.ɵɵelementEnd(),t.ɵɵtemplate(6,Ir,9,4,"fieldset",5),t.ɵɵelementStart(7,"fieldset",2)(8,"legend",3),t.ɵɵtext(9,"tb.rulenode.result-title"),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"div",6)(11,"mat-form-field",7)(12,"mat-label",8),t.ɵɵtext(13,"tb.rulenode.type-field-input"),t.ɵɵelementEnd(),t.ɵɵelementStart(14,"mat-select",9)(15,"mat-select-trigger"),t.ɵɵtext(16),t.ɵɵpipe(17,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(18,Er,5,5,"mat-option",10),t.ɵɵelementEnd(),t.ɵɵtemplate(19,Fr,2,0,"mat-error",11),t.ɵɵelementEnd(),t.ɵɵelementStart(20,"div",12),t.ɵɵtemplate(21,Ar,5,1,"mat-form-field",13),t.ɵɵelementStart(22,"mat-form-field",14)(23,"mat-label",8),t.ɵɵtext(24,"tb.rulenode.key-field-input"),t.ɵɵelementEnd(),t.ɵɵelement(25,"input",15),t.ɵɵelementStart(26,"mat-icon",16),t.ɵɵpipe(27,"translate"),t.ɵɵtext(28,"help"),t.ɵɵelementEnd(),t.ɵɵtemplate(29,kr,2,0,"mat-error",11),t.ɵɵelementEnd()(),t.ɵɵelementStart(30,"div",12)(31,"mat-form-field",17)(32,"mat-label",8),t.ɵɵtext(33,"tb.rulenode.number-floating-point-field-input"),t.ɵɵelementEnd(),t.ɵɵelement(34,"input",18)(35,"mat-hint",19),t.ɵɵpipe(36,"translate"),t.ɵɵpipe(37,"safe"),t.ɵɵelementEnd()(),t.ɵɵtemplate(38,Nr,7,6,"div",20),t.ɵɵelementEnd()()()),2&e){let e;t.ɵɵproperty("formGroup",n.mathFunctionConfigForm),t.ɵɵadvance(5),t.ɵɵproperty("function",n.mathFunctionConfigForm.get("operation").value),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.mathFunctionConfigForm.get("operation").value===n.MathFunction.CUSTOM),t.ɵɵadvance(10),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(17,11,null==(e=n.argumentTypeResultMap.get(n.mathFunctionConfigForm.get("result.type").value))?null:e.name)," "),t.ɵɵadvance(2),t.ɵɵproperty("ngForOf",n.argumentsResult),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.mathFunctionConfigForm.get("result.type").hasError("required")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.mathFunctionConfigForm.get("result").get("type").value===n.ArgumentTypeResult.ATTRIBUTE),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(27,13,"tb.rulenode.math-templatization-tooltip")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.mathFunctionConfigForm.get("result.key").hasError("required")),t.ɵɵadvance(6),t.ɵɵproperty("innerHTML",t.ɵɵpipeBind2(37,17,t.ɵɵpipeBind1(36,15,"tb.rulenode.number-floating-point-field-input-hint"),"html"),t.ɵɵsanitizeHtml),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",t.ɵɵpureFunction2(20,Sr,n.ArgumentTypeResult.ATTRIBUTE,n.ArgumentTypeResult.TIME_SERIES).includes(n.mathFunctionConfigForm.get("result").get("type").value))}},dependencies:t.ɵɵgetComponentDepsFactory(wr),styles:["[_nghost-%COMP%] .fields-group{padding:0 16px 8px;margin:10px 0;border:1px groove rgba(0,0,0,.25);border-radius:4px}[_nghost-%COMP%] .fields-group .mat-mdc-form-field .mat-mdc-form-field-infix{width:100%}[_nghost-%COMP%] .fields-group legend{color:#000000b3;width:fit-content}[_nghost-%COMP%] .fields-group legend+*{display:block}[_nghost-%COMP%] .fields-group legend+*.no-margin-top{margin-top:0}"]})}}function Mr(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",4),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",r.messageTypeNames.get(e)," ")}}e("MathFunctionConfigComponent",wr);class Br extends i{constructor(e,t){super(e),this.store=e,this.fb=t,this.messageTypeNames=f,this.eventOptions=[g.CONNECT_EVENT,g.ACTIVITY_EVENT,g.DISCONNECT_EVENT,g.INACTIVITY_EVENT]}configForm(){return this.deviceState}prepareInputConfig(e){return{event:P(e?.event)?e.event:g.ACTIVITY_EVENT}}onConfigurationSet(e){this.deviceState=this.fb.group({event:[e.event,[N.required]]})}static{this.ɵfac=function(e){return new(e||Br)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Br,selectors:[["tb-action-node-device-state-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:7,vars:5,consts:[[3,"formGroup"],["subscriptSizing","dynamic",1,"mat-block"],["formControlName","event"],[3,"value",4,"ngFor","ngForOf"],[3,"value"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"mat-form-field",1)(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-select",2),t.ɵɵtemplate(6,Mr,2,2,"mat-option",3),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.deviceState),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,3,"tb.rulenode.select-device-connectivity-event")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.eventOptions))},dependencies:t.ɵɵgetComponentDepsFactory(Br),encapsulation:2})}}e("DeviceStateConfigComponent",Br);const Vr=(e,t)=>({valText:e,keyText:t});function Or(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",13),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.requiredText," ")}}function Dr(e,n){1&e&&(t.ɵɵelementStart(0,"div",13),t.ɵɵtext(1," tb.rulenode.map-fields-required "),t.ɵɵelementEnd())}function Lr(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",13),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind2(2,1,"tb.key-val.unique-key-value-pair-error",t.ɵɵpureFunction2(4,Vr,e.valText,e.keyText))," ")}}function Pr(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",14)(1,"mat-form-field",15),t.ɵɵelement(2,"input",16),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-form-field",15),t.ɵɵelement(4,"input",16),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",17)(6,"button",18),t.ɵɵpipe(7,"translate"),t.ɵɵlistener("click",(function(){const n=t.ɵɵrestoreView(e).index,r=t.ɵɵnextContext();return t.ɵɵresetView(r.removeKeyVal(n))})),t.ɵɵelementStart(8,"mat-icon"),t.ɵɵtext(9,"delete"),t.ɵɵelementEnd()()()()}if(2&e){const e=n.$implicit,r=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵproperty("placeholder",r.keyText+"*")("formControl",e.get("key")),t.ɵɵadvance(2),t.ɵɵproperty("placeholder",r.valText+"*")("formControl",e.get("value")),t.ɵɵadvance(2),t.ɵɵclassProp("tb-hidden",1===r.keyValsFormArray().controls.length),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(7,8,"tb.key-val.remove-mapping-entry")),t.ɵɵproperty("disabled",r.disabled)}}function Rr(e,n){if(1&e&&t.ɵɵelement(0,"tb-example-hint",19),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("hintText",e.hintText)("popupHelpLink",e.popupHelpLink)}}class _r{constructor(e,t){this.injector=e,this.fb=t,this.propagateChange=()=>{},this.destroy$=new Y,this.disabled=!1,this.uniqueKeyValuePairValidator=!1,this.required=!1,this.duplicateValuesValidator=e=>e.controls.key.value===e.controls.value.value&&e.controls.key.value&&e.controls.value.value?{uniqueKeyValuePair:!0}:null,this.oneMapRequiredValidator=e=>e.get("keyVals").value.length,this.propagateNestedErrors=e=>{if(this.kvListFormGroup&&this.kvListFormGroup.get("keyVals")&&"VALID"===this.kvListFormGroup.get("keyVals")?.status)return null;const t={};if(this.kvListFormGroup&&this.kvListFormGroup.setErrors(null),e instanceof w||e instanceof M){if(e.errors)for(const n of Object.keys(e.errors))t[n]=!0;for(const n of Object.keys(e.controls)){const r=this.propagateNestedErrors(e.controls[n]);if(r&&Object.keys(r).length)for(const e of Object.keys(r))t[e]=!0}return t}if(e.errors)for(const n of Object.keys(e.errors))t[n]=!0;return R(t,{})?null:t}}ngOnInit(){this.ngControl=this.injector.get(B),null!=this.ngControl&&(this.ngControl.valueAccessor=this),this.kvListFormGroup=this.fb.group({keyVals:this.fb.array([])},{validators:[this.propagateNestedErrors,this.oneMapRequiredValidator]}),this.kvListFormGroup.valueChanges.pipe(W(this.destroy$)).subscribe((()=>{this.updateModel()}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}keyValsFormArray(){return this.kvListFormGroup.get("keyVals")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.kvListFormGroup.disable({emitEvent:!1}):this.kvListFormGroup.enable({emitEvent:!1})}writeValue(e){const t=Object.keys(e).map((t=>({key:t,value:e[t]})));if(this.keyValsFormArray().length===t.length)this.keyValsFormArray().patchValue(t,{emitEvent:!1});else{const e=[];t.forEach((t=>{e.push(this.fb.group({key:[t.key,[N.required,N.pattern(/(?:.|\s)*\S(&:.|\s)*/)]],value:[t.value,[N.required,N.pattern(/(?:.|\s)*\S(&:.|\s)*/)]]},{validators:this.uniqueKeyValuePairValidator?[this.duplicateValuesValidator]:[]}))})),this.kvListFormGroup.setControl("keyVals",this.fb.array(e,this.propagateNestedErrors),{emitEvent:!1})}}removeKeyVal(e){this.keyValsFormArray().removeAt(e)}addKeyVal(){this.keyValsFormArray().push(this.fb.group({key:["",[N.required,N.pattern(/(?:.|\s)*\S(&:.|\s)*/)]],value:["",[N.required,N.pattern(/(?:.|\s)*\S(&:.|\s)*/)]]},{validators:this.uniqueKeyValuePairValidator?[this.duplicateValuesValidator]:[]}))}validate(){const e=this.kvListFormGroup.get("keyVals").value;if(!e.length&&this.required)return{kvMapRequired:!0};if(!this.kvListFormGroup.valid)return{kvFieldsRequired:!0};if(this.uniqueKeyValuePairValidator)for(const t of e)if(t.key===t.value)return{uniqueKeyValuePair:!0};return null}updateModel(){const e=this.kvListFormGroup.get("keyVals").value;if(this.required&&!e.length||!this.kvListFormGroup.valid)this.propagateChange(null);else{const t={};e.forEach((e=>{t[e.key]=e.value})),this.propagateChange(t)}}static{this.ɵfac=function(e){return new(e||_r)(t.ɵɵdirectiveInject(t.Injector),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:_r,selectors:[["tb-kv-map-config"]],inputs:{disabled:"disabled",uniqueKeyValuePairValidator:"uniqueKeyValuePairValidator",labelText:"labelText",requiredText:"requiredText",keyText:"keyText",keyRequiredText:"keyRequiredText",valText:"valText",valRequiredText:"valRequiredText",hintText:"hintText",popupHelpLink:"popupHelpLink",required:"required"},features:[t.ɵɵProvidersFeature([{provide:V,useExisting:r((()=>_r)),multi:!0},{provide:O,useExisting:r((()=>_r)),multi:!0}])],decls:22,vars:12,consts:[[1,"tb-form-panel","stroked",3,"formGroup"],[1,"tb-form-row","no-padding","no-border","space-between"],[1,"tb-form-panel-title"],["class","tb-form-panel-hint tb-error","translate","",4,"ngIf"],[1,"tb-form-panel","no-border","no-padding"],[1,"tb-form-table"],[1,"tb-form-table-header"],[1,"tb-form-table-header-cell","field-space"],[1,"tb-form-table-header-cell","actions-header"],[1,"tb-form-table-body"],["class","tb-form-table-row",4,"ngFor","ngForOf"],["type","button","mat-stroked-button","","color","primary",3,"click"],[3,"hintText","popupHelpLink",4,"ngIf"],["translate","",1,"tb-form-panel-hint","tb-error"],[1,"tb-form-table-row"],["appearance","outline","subscriptSizing","dynamic",1,"tb-inline-field","field-space"],["matInput","",3,"placeholder","formControl"],[1,"tb-form-table-row-cell-buttons"],["type","button","mat-icon-button","","matTooltipPosition","above",3,"click","disabled","matTooltip"],[3,"hintText","popupHelpLink"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3),t.ɵɵelementEnd(),t.ɵɵtemplate(4,Or,2,1,"div",3)(5,Dr,2,0,"div",3)(6,Lr,3,7,"div",3),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"div",4)(8,"div",5)(9,"div",6)(10,"div",7),t.ɵɵtext(11),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"div",7),t.ɵɵtext(13),t.ɵɵelementEnd(),t.ɵɵelement(14,"div",8),t.ɵɵelementEnd(),t.ɵɵelementStart(15,"div",9),t.ɵɵtemplate(16,Pr,10,10,"div",10),t.ɵɵelementEnd()()(),t.ɵɵelementStart(17,"div")(18,"button",11),t.ɵɵlistener("click",(function(){return n.addKeyVal()})),t.ɵɵtext(19),t.ɵɵpipe(20,"translate"),t.ɵɵelementEnd()(),t.ɵɵtemplate(21,Rr,1,2,"tb-example-hint",12),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.kvListFormGroup),t.ɵɵadvance(3),t.ɵɵtextInterpolate(n.labelText),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.kvListFormGroup.hasError("kvMapRequired")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.kvListFormGroup.hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.kvListFormGroup.hasError("uniqueKeyValuePair")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(n.keyText),t.ɵɵadvance(2),t.ɵɵtextInterpolate(n.valText),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.keyValsFormArray().controls),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(20,10,"tb.key-val.add-mapping-entry")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.popupHelpLink||n.hintText))},dependencies:t.ɵɵgetComponentDepsFactory(_r),styles:["[_nghost-%COMP%] .field-space[_ngcontent-%COMP%]{flex:1 1 50%}[_nghost-%COMP%] .actions-header[_ngcontent-%COMP%]{width:40px}"]})}}e("KvMapConfigComponent",_r),J([h()],_r.prototype,"disabled",void 0),J([h()],_r.prototype,"uniqueKeyValuePairValidator",void 0),J([h()],_r.prototype,"required",void 0);const jr=e=>({inputName:e});function Gr(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",13),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementStart(3,"span",3),t.ɵɵtext(4,"tb.rulenode.relations-query-config-direction-suffix"),t.ɵɵelementEnd()()),2&e){const e=n.$implicit,r=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.directionTypeTranslations.get(e))," ")}}function Kr(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.max-relation-level-error")," "))}function Ur(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.max-relation-level-invalid")," "))}function Hr(e,n){1&e&&(t.ɵɵelementStart(0,"div",14),t.ɵɵpipe(1,"translate"),t.ɵɵelementStart(2,"mat-slide-toggle",15),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(1,2,"tb.rulenode.last-level-device-relation-tooltip")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,4,"alias.last-level-relation")," "))}class zr extends y{get required(){return this.requiredValue}set required(e){this.requiredValue=Z(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.values(d),this.directionTypeTranslations=b,this.entityType=u,this.propagateChange=null}ngOnInit(){this.deviceRelationsQueryFormGroup=this.fb.group({fetchLastLevelOnly:[!1,[]],direction:[null,[N.required]],maxLevel:[null,[N.min(1)]],relationType:[null],deviceTypes:[null,[N.required]]}),this.deviceRelationsQueryFormGroup.valueChanges.subscribe((e=>{this.deviceRelationsQueryFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.deviceRelationsQueryFormGroup.disable({emitEvent:!1}):this.deviceRelationsQueryFormGroup.enable({emitEvent:!1})}writeValue(e){this.deviceRelationsQueryFormGroup.reset(e,{emitEvent:!1})}static{this.ɵfac=function(e){return new(e||zr)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:zr,selectors:[["tb-device-relations-query-config"]],inputs:{disabled:"disabled",required:"required"},features:[t.ɵɵProvidersFeature([{provide:V,useExisting:r((()=>zr)),multi:!0}]),t.ɵɵInheritDefinitionFeature],decls:24,vars:26,consts:[[1,"flex","flex-col",3,"formGroup"],[1,"flex","flex-row","gap-5.5"],["subscriptSizing","dynamic","hideRequiredMarker","",1,"mat-block","max-w-50%","flex-full",2,"min-width","100px"],["translate",""],["required","","formControlName","direction"],[3,"value",4,"ngFor","ngForOf"],["floatLabel","always",1,"mat-block","max-w-50%","flex-full"],["matInput","","type","text","pattern","[0-9]*","inputmode","numeric","min","1","formControlName","maxLevel",3,"placeholder"],[4,"ngIf"],["class","tb-form-row no-border no-padding last-level-slide-toggle",3,"tb-hint-tooltip-icon",4,"ngIf"],["formControlName","relationType",1,"flex-1"],["required","","formControlName","deviceTypes",3,"label","entityType","emptyInputPlaceholder","filledInputPlaceholder"],["matSuffix","","aria-hidden","false","aria-label","help-icon","color","primary",1,"help-icon","margin-8","cursor-pointer",3,"matTooltip"],[3,"value"],[1,"tb-form-row","no-border","no-padding","last-level-slide-toggle",3,"tb-hint-tooltip-icon"],["formControlName","fetchLastLevelOnly",1,"mat-slide"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1)(2,"mat-form-field",2)(3,"mat-label",3),t.ɵɵtext(4,"relation.direction"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-select",4),t.ɵɵtemplate(6,Gr,5,4,"mat-option",5),t.ɵɵelementEnd()(),t.ɵɵelementStart(7,"mat-form-field",6)(8,"mat-label",3),t.ɵɵtext(9,"tb.rulenode.max-relation-level"),t.ɵɵelementEnd(),t.ɵɵelement(10,"input",7),t.ɵɵpipe(11,"translate"),t.ɵɵtemplate(12,Kr,3,3,"mat-error",8)(13,Ur,3,3,"mat-error",8),t.ɵɵelementEnd()(),t.ɵɵtemplate(14,Hr,5,6,"div",9),t.ɵɵelement(15,"tb-relation-type-autocomplete",10),t.ɵɵelementStart(16,"tb-entity-subtype-list",11),t.ɵɵpipe(17,"translate"),t.ɵɵpipe(18,"translate"),t.ɵɵpipe(19,"translate"),t.ɵɵelementStart(20,"mat-icon",12),t.ɵɵpipe(21,"translate"),t.ɵɵpipe(22,"translate"),t.ɵɵtext(23,"help"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.deviceRelationsQueryFormGroup),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",n.directionTypes),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(11,11,"tb.rulenode.unlimited-level")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.deviceRelationsQueryFormGroup.get("maxLevel").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.deviceRelationsQueryFormGroup.get("maxLevel").invalid),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.deviceRelationsQueryFormGroup.get("maxLevel").value>1),t.ɵɵadvance(2),t.ɵɵproperty("label",t.ɵɵpipeBind1(17,13,"tb.rulenode.device-profiles"))("entityType",n.entityType.DEVICE)("emptyInputPlaceholder",t.ɵɵpipeBind1(18,15,"tb.rulenode.add-device-profile"))("filledInputPlaceholder",t.ɵɵpipeBind1(19,17,"tb.rulenode.add-device-profile")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind2(22,21,"tb.rulenode.chip-help",t.ɵɵpureFunction1(24,jr,t.ɵɵpipeBind1(21,19,"tb.rulenode.device-profile")))))},dependencies:t.ɵɵgetComponentDepsFactory(zr),styles:["[_nghost-%COMP%] .last-level-slide-toggle[_ngcontent-%COMP%]{margin:8px 0 24px}"]})}}function $r(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",13),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementStart(3,"span",4),t.ɵɵtext(4,"tb.rulenode.relations-query-config-direction-suffix"),t.ɵɵelementEnd()()),2&e){const e=n.$implicit,r=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.directionTypeTranslations.get(e))," ")}}function Qr(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.max-relation-level-error")," "))}function Jr(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.max-relation-level-invalid")," "))}function Yr(e,n){1&e&&(t.ɵɵelementStart(0,"div",14),t.ɵɵpipe(1,"translate"),t.ɵɵelementStart(2,"mat-slide-toggle",15),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(1,2,"tb.rulenode.last-level-relation-tooltip")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,4,"alias.last-level-relation")," "))}e("DeviceRelationsQueryConfigComponent",zr);class Wr extends y{get required(){return this.requiredValue}set required(e){this.requiredValue=Z(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.values(d),this.directionTypeTranslations=b,this.propagateChange=null}ngOnInit(){this.relationsQueryFormGroup=this.fb.group({fetchLastLevelOnly:[!1,[]],direction:[null,[N.required]],maxLevel:[null,[N.min(1)]],filters:[null]}),this.relationsQueryFormGroup.valueChanges.subscribe((e=>{this.relationsQueryFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.relationsQueryFormGroup.disable({emitEvent:!1}):this.relationsQueryFormGroup.enable({emitEvent:!1})}writeValue(e){this.relationsQueryFormGroup.reset(e||{},{emitEvent:!1})}static{this.ɵfac=function(e){return new(e||Wr)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Wr,selectors:[["tb-relations-query-config"]],inputs:{disabled:"disabled",required:"required"},features:[t.ɵɵProvidersFeature([{provide:V,useExisting:r((()=>Wr)),multi:!0}]),t.ɵɵInheritDefinitionFeature],decls:22,vars:9,consts:[[1,"tb-form-panel","stroked",3,"formGroup"],["translate","",1,"tb-form-panel-title","tb-required"],[1,"flex","flex-row","gap-4"],["hideRequiredMarker","",1,"mat-block","max-w-50%","flex-full",2,"min-width","100px"],["translate",""],["required","","formControlName","direction"],[3,"value",4,"ngFor","ngForOf"],["floatLabel","always",1,"mat-block","max-w-50%","flex-full"],["matInput","","type","text","pattern","[0-9]*","min","1","inputmode","numeric","formControlName","maxLevel",3,"placeholder"],[4,"ngIf"],["class","tb-form-row no-border no-padding last-level-slide-toggle",3,"tb-hint-tooltip-icon",4,"ngIf"],["translate","",1,"tb-form-panel-title"],["formControlName","filters"],[3,"value"],[1,"tb-form-row","no-border","no-padding","last-level-slide-toggle",3,"tb-hint-tooltip-icon"],["formControlName","fetchLastLevelOnly",1,"mat-slide"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1),t.ɵɵtext(2,"tb.rulenode.relations-query"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"section")(4,"div",2)(5,"mat-form-field",3)(6,"mat-label",4),t.ɵɵtext(7,"relation.direction"),t.ɵɵelementEnd(),t.ɵɵelementStart(8,"mat-select",5),t.ɵɵtemplate(9,$r,5,4,"mat-option",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"mat-form-field",7)(11,"mat-label",4),t.ɵɵtext(12,"tb.rulenode.max-relation-level"),t.ɵɵelementEnd(),t.ɵɵelement(13,"input",8),t.ɵɵpipe(14,"translate"),t.ɵɵtemplate(15,Qr,3,3,"mat-error",9)(16,Jr,3,3,"mat-error",9),t.ɵɵelementEnd()(),t.ɵɵtemplate(17,Yr,5,6,"div",10),t.ɵɵelementEnd(),t.ɵɵelementStart(18,"section",0)(19,"div",11),t.ɵɵtext(20,"relation.relation-filters"),t.ɵɵelementEnd(),t.ɵɵelement(21,"tb-relation-filters",12),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.relationsQueryFormGroup),t.ɵɵadvance(9),t.ɵɵproperty("ngForOf",n.directionTypes),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(14,7,"tb.rulenode.unlimited-level")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.relationsQueryFormGroup.get("maxLevel").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.relationsQueryFormGroup.get("maxLevel").invalid),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.relationsQueryFormGroup.get("maxLevel").value>1),t.ɵɵadvance(),t.ɵɵproperty("formGroup",n.relationsQueryFormGroup))},dependencies:t.ɵɵgetComponentDepsFactory(Wr),encapsulation:2})}}e("RelationsQueryConfigComponent",Wr);const Xr=["chipList"],Zr=["messageTypeAutocomplete"],ea=["messageTypeInput"],ta=e=>({inputName:e}),na=e=>({messageType:e});function ra(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-label"),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵtextInterpolate(e.label)}}function aa(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-chip-row",13),t.ɵɵlistener("removed",(function(){const n=t.ɵɵrestoreView(e).$implicit,r=t.ɵɵnextContext();return t.ɵɵresetView(r.remove(n))})),t.ɵɵtext(1),t.ɵɵelementStart(2,"mat-icon",14),t.ɵɵtext(3,"close"),t.ɵɵelementEnd()()}if(2&e){const e=n.$implicit;t.ɵɵproperty("removable",!0),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.name," ")}}function ia(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",15),t.ɵɵelement(1,"span",16),t.ɵɵpipe(2,"highlight"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵproperty("innerHTML",t.ɵɵpipeBind2(2,2,e.name,r.searchText),t.ɵɵsanitizeHtml)}}function oa(e,n){1&e&&(t.ɵɵelementStart(0,"div")(1,"span",21),t.ɵɵtext(2,"tb.rulenode.no-message-types-found"),t.ɵɵelementEnd()())}function la(e,n){if(1&e&&(t.ɵɵelementStart(0,"span"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind2(2,1,"tb.rulenode.no-message-type-matching",t.ɵɵpureFunction1(4,na,e.truncate.transform(e.searchText,!0,6,"...")))," ")}}function sa(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-option",17)(1,"div",18),t.ɵɵlistener("click",(function(n){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.stopPropagation())})),t.ɵɵtemplate(2,oa,3,0,"div",19)(3,la,3,6,"ng-template",null,3,t.ɵɵtemplateRefExtractor),t.ɵɵelementStart(5,"span")(6,"a",20),t.ɵɵlistener("click",(function(n){t.ɵɵrestoreView(e);const r=t.ɵɵnextContext();return t.ɵɵresetView(r.createMessageType(n,r.searchText))})),t.ɵɵtext(7,"tb.rulenode.create-new-message-type"),t.ɵɵelementEnd()()()()}if(2&e){const e=t.ɵɵreference(4),n=t.ɵɵnextContext();t.ɵɵproperty("value",null),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",!n.textIsNotEmpty(n.searchText))("ngIfElse",e)}}function pa(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.select-message-types-required")," "))}class ma extends y{get required(){return this.requiredValue}set required(e){this.requiredValue=Z(e)}constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.truncate=n,this.fb=r,this.placeholder="tb.rulenode.add-message-type",this.separatorKeysCodes=[U,H,z],this.messageTypes=[],this.messageTypesList=[],this.searchText="",this.propagateChange=e=>{},this.messageTypeConfigForm=this.fb.group({messageType:[null]});for(const e of Object.keys(g))this.messageTypesList.push({name:f.get(g[e]),value:e})}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}ngOnInit(){this.filteredMessageTypes=this.messageTypeConfigForm.get("messageType").valueChanges.pipe(ee(""),te((e=>e||"")),ne((e=>this.fetchMessageTypes(e))),re())}setDisabledState(e){this.disabled=e,this.disabled?this.messageTypeConfigForm.disable({emitEvent:!1}):this.messageTypeConfigForm.enable({emitEvent:!1})}writeValue(e){this.searchText="",this.messageTypes.length=0,e&&e.forEach((e=>{const t=this.messageTypesList.find((t=>t.value===e));t?this.messageTypes.push({name:t.name,value:t.value}):this.messageTypes.push({name:e,value:e})}))}displayMessageTypeFn(e){return e?e.name:void 0}textIsNotEmpty(e){return e&&e.length>0}createMessageType(e,t){e.preventDefault(),this.transformMessageType(t)}add(e){this.transformMessageType(e.value)}fetchMessageTypes(e){if(this.searchText=e,this.searchText&&this.searchText.length){const e=this.searchText.toUpperCase();return X(this.messageTypesList.filter((t=>t.name.toUpperCase().includes(e))))}return X(this.messageTypesList)}transformMessageType(e){if((e||"").trim()){let t;const n=e.trim(),r=this.messageTypesList.find((e=>e.name===n));t=r?{name:r.name,value:r.value}:{name:n,value:n},t&&this.addMessageType(t)}this.clear("")}remove(e){const t=this.messageTypes.indexOf(e);t>=0&&(this.messageTypes.splice(t,1),this.updateModel())}selected(e){this.addMessageType(e.option.value),this.clear("")}addMessageType(e){-1===this.messageTypes.findIndex((t=>t.value===e.value))&&(this.messageTypes.push(e),this.updateModel())}onFocus(){this.messageTypeConfigForm.get("messageType").updateValueAndValidity({onlySelf:!0,emitEvent:!0})}clear(e=""){this.messageTypeInput.nativeElement.value=e,this.messageTypeConfigForm.get("messageType").patchValue(null,{emitEvent:!0}),setTimeout((()=>{this.messageTypeInput.nativeElement.blur(),this.messageTypeInput.nativeElement.focus()}),0)}updateModel(){const e=this.messageTypes.map((e=>e.value));this.required?(this.chipList.errorState=!e.length,this.propagateChange(e.length>0?e:null)):(this.chipList.errorState=!1,this.propagateChange(e))}static{this.ɵfac=function(e){return new(e||ma)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(K.TranslateService),t.ɵɵdirectiveInject(v.TruncatePipe),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:ma,selectors:[["tb-message-types-config"]],viewQuery:function(e,n){if(1&e&&(t.ɵɵviewQuery(Xr,5),t.ɵɵviewQuery(Zr,5),t.ɵɵviewQuery(ea,5)),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.chipList=e.first),t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.matAutocomplete=e.first),t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.messageTypeInput=e.first)}},inputs:{required:"required",label:"label",placeholder:"placeholder",disabled:"disabled"},features:[t.ɵɵProvidersFeature([{provide:V,useExisting:r((()=>ma)),multi:!0}]),t.ɵɵInheritDefinitionFeature],decls:20,vars:27,consts:[["chipList",""],["messageTypeInput","","origin","matAutocompleteOrigin"],["messageTypeAutocomplete","matAutocomplete"],["searchNotEmpty",""],[2,"width","100%",3,"formGroup"],[4,"ngIf"],[3,"required"],[3,"removable","removed",4,"ngFor","ngForOf"],["matInput","","type","text","formControlName","messageType","matAutocompleteOrigin","",3,"focusin","matChipInputTokenEnd","placeholder","matAutocompleteConnectedTo","matAutocomplete","matChipInputFor","matChipInputSeparatorKeyCodes"],[1,"tb-autocomplete",3,"optionSelected","displayWith"],[3,"value",4,"ngFor","ngForOf"],["class","tb-not-found",3,"value",4,"ngIf"],["aria-hidden","false","aria-label","help-icon","matSuffix","","color","primary",1,"help-icon","margin-8","cursor-pointer",3,"matTooltip"],[3,"removed","removable"],["matChipRemove",""],[3,"value"],[3,"innerHTML"],[1,"tb-not-found",3,"value"],[1,"tb-not-found-content",3,"click"],[4,"ngIf","ngIfElse"],["translate","",3,"click"],["translate",""]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-form-field",4),t.ɵɵtemplate(1,ra,2,1,"mat-label",5),t.ɵɵelementStart(2,"mat-chip-grid",6,0),t.ɵɵtemplate(4,aa,4,2,"mat-chip-row",7),t.ɵɵelementStart(5,"input",8,1),t.ɵɵpipe(8,"translate"),t.ɵɵlistener("focusin",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.onFocus())}))("matChipInputTokenEnd",(function(r){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.add(r))})),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"mat-autocomplete",9,2),t.ɵɵlistener("optionSelected",(function(r){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.selected(r))})),t.ɵɵtemplate(11,ia,3,5,"mat-option",10),t.ɵɵpipe(12,"async"),t.ɵɵtemplate(13,sa,8,3,"mat-option",11),t.ɵɵpipe(14,"async"),t.ɵɵelementEnd(),t.ɵɵelementStart(15,"mat-icon",12),t.ɵɵpipe(16,"translate"),t.ɵɵpipe(17,"translate"),t.ɵɵtext(18,"help"),t.ɵɵelementEnd(),t.ɵɵtemplate(19,pa,3,3,"mat-error",5),t.ɵɵelementEnd()}if(2&e){let e;const r=t.ɵɵreference(3),a=t.ɵɵreference(7),i=t.ɵɵreference(10);t.ɵɵproperty("formGroup",n.messageTypeConfigForm),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.label),t.ɵɵadvance(),t.ɵɵproperty("required",n.required),t.ɵɵadvance(2),t.ɵɵproperty("ngForOf",n.messageTypes),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(8,14,n.placeholder)),t.ɵɵproperty("matAutocompleteConnectedTo",a)("matAutocomplete",i)("matChipInputFor",r)("matChipInputSeparatorKeyCodes",n.separatorKeysCodes),t.ɵɵadvance(4),t.ɵɵproperty("displayWith",n.displayMessageTypeFn),t.ɵɵadvance(2),t.ɵɵproperty("ngForOf",t.ɵɵpipeBind1(12,16,n.filteredMessageTypes)),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",0===(null==(e=t.ɵɵpipeBind1(14,18,n.filteredMessageTypes))?null:e.length)),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind2(17,22,"tb.rulenode.chip-help",t.ɵɵpureFunction1(25,ta,t.ɵɵpipeBind1(16,20,"tb.rulenode.message-type")))),t.ɵɵadvance(4),t.ɵɵproperty("ngIf",r.errorState)}},dependencies:t.ɵɵgetComponentDepsFactory(ma),encapsulation:2})}}function da(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",12),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext(2);t.ɵɵproperty("value",e)("disabled","cert.PEM"===e&&r.disableCertPemCredentials),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,3,r.credentialsTypeTranslationsMap.get(e))," ")}}function ua(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.credentials-type-required")," "))}function ca(e,t){}function fa(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.username-required")," "))}function ga(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.password-required")," "))}function ha(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",4)(1,"mat-label",2),t.ɵɵtext(2,"tb.rulenode.username"),t.ɵɵelementEnd(),t.ɵɵelement(3,"input",13),t.ɵɵtemplate(4,fa,3,3,"mat-error",7),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field",4)(6,"mat-label",2),t.ɵɵtext(7,"tb.rulenode.password"),t.ɵɵelementEnd(),t.ɵɵelement(8,"input",14)(9,"tb-toggle-password",15),t.ɵɵtemplate(10,ga,3,3,"mat-error",7),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(4),t.ɵɵproperty("ngIf",e.credentialsConfigFormGroup.get("username").hasError("required")),t.ɵɵadvance(4),t.ɵɵproperty("required",e.passwordFieldRequired),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.credentialsConfigFormGroup.get("password").hasError("required"))}}function ya(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",16),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"tb-file-input",17),t.ɵɵpipe(4,"translate"),t.ɵɵpipe(5,"translate"),t.ɵɵlistener("fileNameChanged",(function(n){t.ɵɵrestoreView(e);const r=t.ɵɵnextContext(2);return t.ɵɵresetView(r.credentialsConfigFormGroup.get("caCertFileName").setValue(n))})),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"tb-file-input",18),t.ɵɵpipe(7,"translate"),t.ɵɵpipe(8,"translate"),t.ɵɵlistener("fileNameChanged",(function(n){t.ɵɵrestoreView(e);const r=t.ɵɵnextContext(2);return t.ɵɵresetView(r.credentialsConfigFormGroup.get("certFileName").setValue(n))})),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"tb-file-input",19),t.ɵɵpipe(10,"translate"),t.ɵɵpipe(11,"translate"),t.ɵɵlistener("fileNameChanged",(function(n){t.ɵɵrestoreView(e);const r=t.ɵɵnextContext(2);return t.ɵɵresetView(r.credentialsConfigFormGroup.get("privateKeyFileName").setValue(n))})),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"mat-form-field",4)(13,"mat-label",2),t.ɵɵtext(14,"tb.rulenode.private-key-password"),t.ɵɵelementEnd(),t.ɵɵelement(15,"input",20)(16,"tb-toggle-password",15),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,10,"tb.rulenode.credentials-pem-hint")),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(4,12,"tb.rulenode.ca-cert")),t.ɵɵpropertyInterpolate("dropLabel",t.ɵɵpipeBind1(5,14,"tb.rulenode.drop-file")),t.ɵɵproperty("existingFileName",e.credentialsConfigFormGroup.get("caCertFileName").value),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(7,16,"tb.rulenode.cert")),t.ɵɵpropertyInterpolate("dropLabel",t.ɵɵpipeBind1(8,18,"tb.rulenode.drop-file")),t.ɵɵproperty("existingFileName",e.credentialsConfigFormGroup.get("certFileName").value),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(10,20,"tb.rulenode.private-key")),t.ɵɵpropertyInterpolate("dropLabel",t.ɵɵpipeBind1(11,22,"tb.rulenode.drop-file")),t.ɵɵproperty("existingFileName",e.credentialsConfigFormGroup.get("privateKeyFileName").value)}}function ba(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",4)(1,"mat-label",2),t.ɵɵtext(2,"tb.rulenode.credentials-type"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-select",5),t.ɵɵtemplate(4,da,3,5,"mat-option",6),t.ɵɵelementEnd(),t.ɵɵtemplate(5,ua,3,3,"mat-error",7),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"section",8),t.ɵɵtemplate(7,ca,0,0,"ng-template",9)(8,ha,11,3,"ng-template",10)(9,ya,17,24,"ng-template",11),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(4),t.ɵɵproperty("ngForOf",e.allCredentialsTypes),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.credentialsConfigFormGroup.get("type").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngSwitch",e.credentialsConfigFormGroup.get("type").value)}}e("MessageTypesConfigComponent",ma);class va extends y{get required(){return this.requiredValue}set required(e){this.requiredValue=Z(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.subscriptions=[],this.disableCertPemCredentials=!1,this.passwordFieldRequired=!0,this.allCredentialsTypes=Mt,this.credentialsTypeTranslationsMap=Bt,this.propagateChange=e=>{}}ngOnInit(){this.credentialsConfigFormGroup=this.fb.group({type:[null,[N.required]],username:[null,[]],password:[null,[]],caCert:[null,[]],caCertFileName:[null,[]],privateKey:[null,[]],privateKeyFileName:[null,[]],cert:[null,[]],certFileName:[null,[]]}),this.subscriptions.push(this.credentialsConfigFormGroup.valueChanges.subscribe((()=>{this.updateView()}))),this.subscriptions.push(this.credentialsConfigFormGroup.get("type").valueChanges.subscribe((()=>{this.credentialsTypeChanged()})))}ngOnChanges(e){for(const t of Object.keys(e)){const n=e[t];if(!n.firstChange&&n.currentValue!==n.previousValue&&n.currentValue&&"disableCertPemCredentials"===t){"cert.PEM"===this.credentialsConfigFormGroup.get("type").value&&setTimeout((()=>{this.credentialsConfigFormGroup.get("type").patchValue("anonymous",{emitEvent:!0})}))}}}ngOnDestroy(){this.subscriptions.forEach((e=>e.unsubscribe()))}writeValue(e){P(e)&&(this.credentialsConfigFormGroup.reset(e,{emitEvent:!1}),this.updateValidators())}setDisabledState(e){e?this.credentialsConfigFormGroup.disable({emitEvent:!1}):(this.credentialsConfigFormGroup.enable({emitEvent:!1}),this.updateValidators())}updateView(){let e=this.credentialsConfigFormGroup.value;const t=e.type;switch(t){case"anonymous":e={type:t};break;case"basic":e={type:t,username:e.username,password:e.password};break;case"cert.PEM":delete e.username}this.propagateChange(e)}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}validate(e){return this.credentialsConfigFormGroup.valid?null:{credentialsConfig:{valid:!1}}}credentialsTypeChanged(){this.credentialsConfigFormGroup.patchValue({username:null,password:null,caCert:null,caCertFileName:null,privateKey:null,privateKeyFileName:null,cert:null,certFileName:null}),this.updateValidators()}updateValidators(e=!1){const t=this.credentialsConfigFormGroup.get("type").value;switch(e&&this.credentialsConfigFormGroup.reset({type:t},{emitEvent:!1}),this.credentialsConfigFormGroup.setValidators([]),this.credentialsConfigFormGroup.get("username").setValidators([]),this.credentialsConfigFormGroup.get("password").setValidators([]),t){case"anonymous":break;case"basic":this.credentialsConfigFormGroup.get("username").setValidators([N.required]),this.credentialsConfigFormGroup.get("password").setValidators(this.passwordFieldRequired?[N.required]:[]);break;case"cert.PEM":this.credentialsConfigFormGroup.setValidators([this.requiredFilesSelected(N.required,[["caCert","caCertFileName"],["privateKey","privateKeyFileName","cert","certFileName"]])])}this.credentialsConfigFormGroup.get("username").updateValueAndValidity({emitEvent:e}),this.credentialsConfigFormGroup.get("password").updateValueAndValidity({emitEvent:e}),this.credentialsConfigFormGroup.updateValueAndValidity({emitEvent:e})}requiredFilesSelected(e,t=null){return n=>{t||(t=[Object.keys(n.controls)]);return n?.controls&&t.some((t=>t.every((t=>!e(n.controls[t])))))?null:{notAllRequiredFilesSelected:!0}}}static{this.ɵfac=function(e){return new(e||va)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:va,selectors:[["tb-credentials-config"]],inputs:{required:"required",disableCertPemCredentials:"disableCertPemCredentials",passwordFieldRequired:"passwordFieldRequired"},features:[t.ɵɵProvidersFeature([{provide:V,useExisting:r((()=>va)),multi:!0},{provide:O,useExisting:r((()=>va)),multi:!0}]),t.ɵɵInheritDefinitionFeature,t.ɵɵNgOnChangesFeature],decls:9,vars:4,consts:[[1,"flex","flex-col",3,"formGroup"],[1,"tb-credentials-config-panel-group"],["translate",""],["matExpansionPanelContent",""],[1,"mat-block"],["formControlName","type","required",""],[3,"value","disabled",4,"ngFor","ngForOf"],[4,"ngIf"],[1,"flex","flex-col",3,"ngSwitch"],["ngSwitchCase","anonymous"],["ngSwitchCase","basic"],["ngSwitchCase","cert.PEM"],[3,"value","disabled"],["required","","matInput","","formControlName","username"],["type","password","matInput","","formControlName","password",3,"required"],["matSuffix",""],[1,"tb-hint"],["formControlName","caCert","inputId","caCertSelect","noFileText","tb.rulenode.no-file",3,"fileNameChanged","existingFileName","label","dropLabel"],["formControlName","cert","inputId","CertSelect","noFileText","tb.rulenode.no-file",3,"fileNameChanged","existingFileName","label","dropLabel"],["formControlName","privateKey","inputId","privateKeySelect","noFileText","tb.rulenode.no-file",2,"padding-bottom","8px",3,"fileNameChanged","existingFileName","label","dropLabel"],["type","password","matInput","","formControlName","password"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"mat-expansion-panel",1)(2,"mat-expansion-panel-header")(3,"mat-panel-title",2),t.ɵɵtext(4,"tb.rulenode.credentials"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-panel-description"),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd()(),t.ɵɵtemplate(8,ba,10,3,"ng-template",3),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.credentialsConfigFormGroup),t.ɵɵadvance(6),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(7,2,n.credentialsTypeTranslationsMap.get(n.credentialsConfigFormGroup.get("type").value))," "))},dependencies:t.ɵɵgetComponentDepsFactory(va),encapsulation:2})}}function xa(e,n){1&e&&(t.ɵɵelementStart(0,"button",22),t.ɵɵpipe(1,"translate"),t.ɵɵelementStart(2,"mat-icon"),t.ɵɵtext(3,"drag_handle"),t.ɵɵelementEnd()()),2&e&&t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,1,"action.drag"))}function Ca(e,n){if(1&e&&(t.ɵɵelementStart(0,"span",23),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext().$implicit;t.ɵɵadvance(),t.ɵɵtextInterpolate1("",e.get("name").value,".")}}function Sa(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",24),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementStart(3,"small",25),t.ɵɵtext(4),t.ɵɵelementEnd()()),2&e){const e=n.$implicit,r=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,3,r.argumentTypeMap.get(e).name)," "),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",r.argumentTypeMap.get(e).description," ")}}function Ta(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",13),t.ɵɵtext(1," tb.rulenode.argument-source-field-input-required "),t.ɵɵelementEnd())}function Ia(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",13),t.ɵɵtext(1," tb.rulenode.argument-key-field-input-required "),t.ɵɵelementEnd())}function Ea(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",26)(1,"mat-label",13),t.ɵɵtext(2,"tb.rulenode.argument-key-field-input"),t.ɵɵelementEnd(),t.ɵɵelement(3,"input",27),t.ɵɵelementStart(4,"mat-icon",28),t.ɵɵpipe(5,"translate"),t.ɵɵtext(6," help "),t.ɵɵelementEnd(),t.ɵɵtemplate(7,Ia,2,0,"mat-error",16),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext().$implicit;t.ɵɵadvance(3),t.ɵɵproperty("formControl",e.get("key")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(5,3,"tb.rulenode.math-templatization-tooltip")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",e.get("key").hasError("required"))}}function Fa(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",13),t.ɵɵtext(1," tb.rulenode.constant-value-field-input-required "),t.ɵɵelementEnd())}function qa(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",29)(1,"mat-label",13),t.ɵɵtext(2,"tb.rulenode.constant-value-field-input"),t.ɵɵelementEnd(),t.ɵɵelement(3,"input",30),t.ɵɵtemplate(4,Fa,2,0,"mat-error",16),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext().$implicit;t.ɵɵadvance(3),t.ɵɵproperty("formControl",e.get("key")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.get("key").hasError("required"))}}function Aa(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",26)(1,"mat-label",13),t.ɵɵtext(2,"tb.rulenode.default-value-field-input"),t.ɵɵelementEnd(),t.ɵɵelement(3,"input",31),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext().$implicit;t.ɵɵadvance(3),t.ɵɵproperty("formControl",e.get("defaultValue"))}}function ka(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",33),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext(3);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.attributeScopeMap.get(e))," ")}}function Na(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error",13),t.ɵɵtext(1," tb.rulenode.attribute-scope-field-input-required "),t.ɵɵelementEnd())}function wa(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",12)(1,"mat-label",13),t.ɵɵtext(2,"tb.rulenode.attribute-scope-field-input"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-select",14),t.ɵɵtemplate(4,ka,3,4,"mat-option",32),t.ɵɵelementEnd(),t.ɵɵtemplate(5,Na,2,0,"mat-error",16),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext().$implicit,n=t.ɵɵnextContext();t.ɵɵadvance(3),t.ɵɵproperty("formControl",e.get("attributeScope")),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",n.attributeScope),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.get("attributeScope").hasError("required"))}}function Ma(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",34),t.ɵɵpipe(1,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext().index,r=t.ɵɵnextContext();return t.ɵɵresetView(r.removeArgument(n))})),t.ɵɵelementStart(2,"mat-icon"),t.ɵɵtext(3,"close"),t.ɵɵelementEnd()()}2&e&&t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,1,"action.remove"))}function Ba(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-list-item",6)(1,"div",7),t.ɵɵtemplate(2,xa,4,3,"button",8),t.ɵɵelementStart(3,"div",9),t.ɵɵtemplate(4,Ca,2,1,"span",10),t.ɵɵelementStart(5,"div",11)(6,"mat-form-field",12)(7,"mat-label",13),t.ɵɵtext(8,"tb.rulenode.argument-source-field-input"),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"mat-select",14)(10,"mat-select-trigger"),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(13,Sa,5,5,"mat-option",15),t.ɵɵelementEnd(),t.ɵɵtemplate(14,Ta,2,0,"mat-error",16),t.ɵɵelementEnd(),t.ɵɵelementStart(15,"div",17),t.ɵɵtemplate(16,Ea,8,5,"mat-form-field",18)(17,qa,5,2,"mat-form-field",19)(18,Aa,4,1,"mat-form-field",18),t.ɵɵelementEnd(),t.ɵɵtemplate(19,wa,6,3,"mat-form-field",20),t.ɵɵelementEnd(),t.ɵɵtemplate(20,Ma,4,3,"button",21),t.ɵɵelementEnd()()()),2&e){let e;const r=n.$implicit,a=t.ɵɵnextContext();t.ɵɵproperty("cdkDragDisabled",a.disabled),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",!a.disabled),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",a.displayArgumentName),t.ɵɵadvance(5),t.ɵɵproperty("formControl",r.get("type")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(12,12,null==(e=a.argumentTypeMap.get(r.get("type").value))?null:e.name)," "),t.ɵɵadvance(2),t.ɵɵproperty("ngForOf",a.arguments),t.ɵɵadvance(),t.ɵɵproperty("ngIf",r.get("type").hasError("required")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",r.get("type").value&&r.get("type").value!==a.ArgumentType.CONSTANT),t.ɵɵadvance(),t.ɵɵproperty("ngIf",r.get("type").value===a.ArgumentType.CONSTANT),t.ɵɵadvance(),t.ɵɵproperty("ngIf",r.get("type").value&&r.get("type").value!==a.ArgumentType.CONSTANT),t.ɵɵadvance(),t.ɵɵproperty("ngIf",r.get("type").value===a.ArgumentType.ATTRIBUTE),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!a.disabled)}}function Va(e,n){1&e&&(t.ɵɵelementStart(0,"div")(1,"span",35),t.ɵɵtext(2,"tb.rulenode.no-arguments-prompt"),t.ɵɵelementEnd()())}e("CredentialsConfigComponent",va);class Oa extends y{get function(){return this.functionValue}set function(e){e&&this.functionValue!==e&&(this.functionValue=e,this.setupArgumentsFormGroup(!0))}constructor(e,t){super(e),this.store=e,this.fb=t,this.maxArgs=16,this.minArgs=1,this.displayArgumentName=!1,this.mathFunctionMap=_t,this.ArgumentType=jt,this.attributeScopeMap=Xt,this.argumentTypeMap=$t,this.arguments=Object.values(jt),this.attributeScope=Object.values(Yt),this.propagateChange=null,this.valueChangeSubscription=[]}ngOnInit(){this.argumentsFormGroup=this.fb.group({arguments:this.fb.array([])}),this.valueChangeSubscription.push(this.argumentsFormGroup.valueChanges.subscribe((()=>{this.updateModel()}))),this.setupArgumentsFormGroup()}onDrop(e){const t=this.argumentsFormArray,n=t.at(e.previousIndex);t.removeAt(e.previousIndex),t.insert(e.currentIndex,n),this.updateArgumentNames()}get argumentsFormArray(){return this.argumentsFormGroup.get("arguments")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.argumentsFormGroup.disable({emitEvent:!1}):(this.argumentsFormGroup.enable({emitEvent:!1}),this.argumentsFormArray.controls.forEach((e=>this.updateArgumentControlValidators(e))))}ngOnDestroy(){this.valueChangeSubscription.length&&this.valueChangeSubscription.forEach((e=>e.unsubscribe()))}writeValue(e){const t=[];e&&e.forEach(((e,n)=>{t.push(this.createArgumentControl(e,n))})),this.argumentsFormGroup.setControl("arguments",this.fb.array(t),{emitEvent:!1}),this.setupArgumentsFormGroup()}removeArgument(e){this.argumentsFormArray.removeAt(e),this.updateArgumentNames()}addArgument(e=!0){const t=this.argumentsFormArray,n=this.createArgumentControl(null,t.length);t.push(n,{emitEvent:e})}validate(e){return this.argumentsFormGroup.valid?null:{argumentsRequired:!0}}setupArgumentsFormGroup(e=!1){if(this.function&&(this.maxArgs=this.mathFunctionMap.get(this.function).maxArgs,this.minArgs=this.mathFunctionMap.get(this.function).minArgs,this.displayArgumentName=this.function===Rt.CUSTOM),this.argumentsFormGroup){for(this.argumentsFormGroup.get("arguments").setValidators([N.minLength(this.minArgs),N.maxLength(this.maxArgs)]);this.argumentsFormArray.length>this.maxArgs;)this.removeArgument(this.maxArgs-1);for(;this.argumentsFormArray.length{this.updateArgumentControlValidators(n),n.get("attributeScope").updateValueAndValidity({emitEvent:!1}),n.get("defaultValue").updateValueAndValidity({emitEvent:!1})}))),n}updateArgumentControlValidators(e){const t=e.get("type").value;t===jt.ATTRIBUTE?e.get("attributeScope").enable({emitEvent:!1}):e.get("attributeScope").disable({emitEvent:!1}),t&&t!==jt.CONSTANT?e.get("defaultValue").enable({emitEvent:!1}):e.get("defaultValue").disable({emitEvent:!1})}updateArgumentNames(){this.argumentsFormArray.controls.forEach(((e,t)=>{e.get("name").setValue(Jt[t])}))}updateModel(){const e=this.argumentsFormArray.value;e.length&&this.argumentsFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}static{this.ɵfac=function(e){return new(e||Oa)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Oa,selectors:[["tb-arguments-map-config"]],inputs:{disabled:"disabled",function:"function"},features:[t.ɵɵProvidersFeature([{provide:V,useExisting:r((()=>Oa)),multi:!0},{provide:O,useExisting:r((()=>Oa)),multi:!0}]),t.ɵɵInheritDefinitionFeature],decls:10,vars:10,consts:[[1,"flex","flex-col"],[2,"max-height","500px","overflow","auto"],["cdkDropList","","cdkDropListOrientation","vertical",1,"tb-drop-list","arguments-list",3,"cdkDropListDropped","formGroup","cdkDropListDisabled"],["formArrayName","arguments","cdkDrag","","class","tb-argument tb-draggable","style","height: 100%",3,"cdkDragDisabled",4,"ngFor","ngForOf"],[4,"ngIf"],["mat-button","","mat-raised-button","","color","primary","type","button","matTooltipPosition","above",3,"click","disabled"],["formArrayName","arguments","cdkDrag","",1,"tb-argument","tb-draggable",2,"height","100%",3,"cdkDragDisabled"],[1,"flex","flex-1","flex-row","items-center","justify-start"],["mat-icon-button","","color","primary","cdkDragHandle","","class","tb-drag-handle handle","style","min-width: 40px; margin: 0","matTooltipPosition","above",3,"matTooltip",4,"ngIf"],[1,"flex","flex-1","flex-row","items-center","justify-start","gap-4"],["style","padding: 0 10px; min-width: 20px;",4,"ngIf"],[1,"flex","flex-1","flex-col"],[1,"mat-block"],["translate",""],["required","",3,"formControl"],["style","border-bottom: 1px solid #eee;",3,"value",4,"ngFor","ngForOf"],["translate","",4,"ngIf"],[1,"flex","flex-1","flex-row","xs:flex-col","gt-xs:gap-4"],["floatLabel","always","class","mat-block gt-xs:max-w-50% gt-xs:flex-full",4,"ngIf"],["floatLabel","always","class","mat-block flex-1",4,"ngIf"],["class","mat-block",4,"ngIf"],["mat-icon-button","","color","primary","style","min-width: 40px;","matTooltipPosition","above",3,"matTooltip","click",4,"ngIf"],["mat-icon-button","","color","primary","cdkDragHandle","","matTooltipPosition","above",1,"tb-drag-handle","handle",2,"min-width","40px","margin","0",3,"matTooltip"],[2,"padding","0 10px","min-width","20px"],[2,"border-bottom","1px solid #eee",3,"value"],[2,"display","block","overflow","hidden","text-overflow","ellipsis","white-space","nowrap"],["floatLabel","always",1,"mat-block","gt-xs:max-w-50%","gt-xs:flex-full"],["matInput","","required","",3,"formControl"],["aria-hidden","false","aria-label","help-icon","matSuffix","","color","primary",1,"help-icon","margin-8","cursor-pointer",3,"matTooltip"],["floatLabel","always",1,"mat-block","flex-1"],["matInput","","required","","step","1","min","0","type","number",3,"formControl"],["matInput","","step","1","type","number",3,"formControl"],[3,"value",4,"ngFor","ngForOf"],[3,"value"],["mat-icon-button","","color","primary","matTooltipPosition","above",2,"min-width","40px",3,"click","matTooltip"],["translate","",1,"tb-prompt","flex","items-center","justify-center"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1)(2,"mat-list",2),t.ɵɵlistener("cdkDropListDropped",(function(e){return n.onDrop(e)})),t.ɵɵtemplate(3,Ba,21,14,"mat-list-item",3),t.ɵɵelementEnd()(),t.ɵɵtemplate(4,Va,3,0,"div",4),t.ɵɵelementStart(5,"button",5),t.ɵɵlistener("click",(function(){return n.addArgument()})),t.ɵɵelementStart(6,"mat-icon"),t.ɵɵtext(7,"add"),t.ɵɵelementEnd(),t.ɵɵtext(8),t.ɵɵpipe(9,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵadvance(),t.ɵɵclassProp("readonly",n.disabled),t.ɵɵadvance(),t.ɵɵproperty("formGroup",n.argumentsFormGroup)("cdkDropListDisabled",n.disabled),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",n.argumentsFormArray.controls),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!n.argumentsFormArray.length),t.ɵɵadvance(),t.ɵɵproperty("disabled",n.argumentsFormArray.length>=n.maxArgs),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(9,8,"action.add")," "))},dependencies:t.ɵɵgetComponentDepsFactory(Oa),styles:["[_nghost-%COMP%] .mat-mdc-list-item.tb-argument[_ngcontent-%COMP%]{border:solid rgba(0,0,0,.25) 1px;border-radius:4px;padding:10px 0;margin-bottom:16px}[_nghost-%COMP%] .arguments-list[_ngcontent-%COMP%]{padding:0}"]})}}e("ArgumentsMapConfigComponent",Oa);const Da=["operationInput"];function La(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"button",9),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.clear())})),t.ɵɵelementStart(1,"mat-icon",10),t.ɵɵtext(2,"close"),t.ɵɵelementEnd()()}}function Pa(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",11),t.ɵɵelement(1,"span",12),t.ɵɵpipe(2,"highlight"),t.ɵɵelementStart(3,"small",13),t.ɵɵtext(4),t.ɵɵelementEnd()()),2&e){const e=n.$implicit,r=t.ɵɵnextContext();t.ɵɵproperty("value",e.value),t.ɵɵadvance(),t.ɵɵproperty("innerHTML",t.ɵɵpipeBind2(2,3,e.value+" | "+e.name,r.searchText),t.ɵɵsanitizeHtml),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",e.description," ")}}function Ra(e,n){1&e&&(t.ɵɵelementStart(0,"mat-option",11)(1,"span",3),t.ɵɵtext(2,"tb.rulenode.no-option-found"),t.ɵɵelementEnd()()),2&e&&t.ɵɵproperty("value",null)}class _a extends y{get required(){return this.requiredValue}set required(e){this.requiredValue=Z(e)}constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.injector=n,this.fb=r,this.searchText="",this.dirty=!1,this.mathOperation=[..._t.values()],this.propagateChange=null}ngOnInit(){this.mathFunctionForm=this.fb.group({operation:[""]}),this.filteredOptions=this.mathFunctionForm.get("operation").valueChanges.pipe(ae((e=>{let t;t="string"==typeof e&&Rt[e]?Rt[e]:null,this.updateView(t)})),te((e=>(this.searchText=e||"",e?this._filter(e):this.mathOperation.slice()))))}_filter(e){const t=e.toLowerCase();return this.mathOperation.filter((e=>e.name.toLowerCase().includes(t)||e.value.toLowerCase().includes(t)))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.mathFunctionForm.disable({emitEvent:!1}):this.mathFunctionForm.enable({emitEvent:!1})}mathFunctionDisplayFn(e){if(e){const t=_t.get(e);return t.value+" | "+t.name}return""}writeValue(e){this.modelValue=e,this.mathFunctionForm.get("operation").setValue(e,{emitEvent:!1}),this.dirty=!0}updateView(e){this.modelValue!==e&&(this.modelValue=e,this.propagateChange(this.modelValue))}onFocus(){this.dirty&&(this.mathFunctionForm.get("operation").updateValueAndValidity({onlySelf:!0}),this.dirty=!1)}clear(){this.mathFunctionForm.get("operation").patchValue(""),setTimeout((()=>{this.operationInput.nativeElement.blur(),this.operationInput.nativeElement.focus()}),0)}static{this.ɵfac=function(e){return new(e||_a)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(K.TranslateService),t.ɵɵdirectiveInject(t.Injector),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:_a,selectors:[["tb-math-function-autocomplete"]],viewQuery:function(e,n){if(1&e&&t.ɵɵviewQuery(Da,7),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.operationInput=e.first)}},inputs:{required:"required",disabled:"disabled"},features:[t.ɵɵProvidersFeature([{provide:V,useExisting:r((()=>_a)),multi:!0}]),t.ɵɵInheritDefinitionFeature],decls:12,vars:11,consts:[["operationInput",""],["auto","matAutocomplete"],[1,"mat-block",3,"formGroup"],["translate",""],["type","text","matInput","","formControlName","operation",3,"focusin","required","matAutocomplete"],["type","button","matSuffix","","mat-icon-button","","aria-label","Clear",3,"click",4,"ngIf"],[1,"tb-autocomplete",3,"displayWith"],[3,"value",4,"ngFor","ngForOf"],[3,"value",4,"ngIf"],["type","button","matSuffix","","mat-icon-button","","aria-label","Clear",3,"click"],[1,"material-icons"],[3,"value"],[3,"innerHTML"],[2,"display","block","overflow","hidden","text-overflow","ellipsis","white-space","nowrap"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-form-field",2)(1,"mat-label",3),t.ɵɵtext(2,"tb.rulenode.functions-field-input"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"input",4,0),t.ɵɵlistener("focusin",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.onFocus())})),t.ɵɵelementEnd(),t.ɵɵtemplate(5,La,3,0,"button",5),t.ɵɵelementStart(6,"mat-autocomplete",6,1),t.ɵɵtemplate(8,Pa,5,6,"mat-option",7),t.ɵɵpipe(9,"async"),t.ɵɵtemplate(10,Ra,3,1,"mat-option",8),t.ɵɵpipe(11,"async"),t.ɵɵelementEnd()()}if(2&e){let e;const r=t.ɵɵreference(7);t.ɵɵproperty("formGroup",n.mathFunctionForm),t.ɵɵadvance(3),t.ɵɵproperty("required",n.required)("matAutocomplete",r),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.mathFunctionForm.get("operation").value),t.ɵɵadvance(),t.ɵɵproperty("displayWith",n.mathFunctionDisplayFn),t.ɵɵadvance(2),t.ɵɵproperty("ngForOf",t.ɵɵpipeBind1(9,7,n.filteredOptions)),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",!(null!=(e=t.ɵɵpipeBind1(11,9,n.filteredOptions))&&e.length))}},dependencies:t.ɵɵgetComponentDepsFactory(_a),encapsulation:2})}}function ja(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",8),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.name," ")}}function Ga(e,n){if(1&e&&(t.ɵɵelementStart(0,"button",9),t.ɵɵpipe(1,"translate"),t.ɵɵelementStart(2,"mat-icon",10),t.ɵɵtext(3,"content_copy "),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(1,2,"tb.rulenode.copy-message-type")),t.ɵɵproperty("cbContent",e.messageTypeFormGroup.get("messageType").value)}}function Ka(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.message-type-value-required")," "))}function Ua(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.message-type-value-max-length")," "))}e("MathFunctionAutocompleteComponent",_a);class Ha{set required(e){this.requiredValue!==e&&(this.requiredValue=e,this.updateValidators())}get required(){return this.requiredValue}constructor(e){this.fb=e,this.subscriptSizing="fixed",this.messageTypes=[{name:"Post attributes",value:"POST_ATTRIBUTES_REQUEST"},{name:"Post telemetry",value:"POST_TELEMETRY_REQUEST"},{name:"Custom",value:""}],this.propagateChange=()=>{},this.destroy$=new Y,this.messageTypeFormGroup=this.fb.group({messageTypeAlias:[null,[N.required]],messageType:[{value:null,disabled:!0},[N.maxLength(255)]]}),this.messageTypeFormGroup.get("messageTypeAlias").valueChanges.pipe(W(this.destroy$)).subscribe((e=>this.updateMessageTypeValue(e))),this.messageTypeFormGroup.valueChanges.pipe(W(this.destroy$)).subscribe((()=>this.updateView()))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}registerOnTouched(e){}registerOnChange(e){this.propagateChange=e}writeValue(e){this.modelValue=e;let t=this.messageTypes.find((t=>t.value===e));t||(t=this.messageTypes.find((e=>""===e.value))),this.messageTypeFormGroup.get("messageTypeAlias").patchValue(t,{emitEvent:!1}),this.messageTypeFormGroup.get("messageType").patchValue(e,{emitEvent:!1})}validate(){return this.messageTypeFormGroup.valid?null:{messageTypeInvalid:!0}}setDisabledState(e){this.disabled=e,e?this.messageTypeFormGroup.disable({emitEvent:!1}):(this.messageTypeFormGroup.enable({emitEvent:!1}),"Custom"!==this.messageTypeFormGroup.get("messageTypeAlias").value?.name&&this.messageTypeFormGroup.get("messageType").disable({emitEvent:!1}))}updateView(){const e=this.messageTypeFormGroup.getRawValue().messageType;this.modelValue!==e&&(this.modelValue=e,this.propagateChange(this.modelValue))}updateValidators(){this.messageTypeFormGroup.get("messageType").setValidators(this.required?[N.required,N.maxLength(255)]:[N.maxLength(255)]),this.messageTypeFormGroup.get("messageType").updateValueAndValidity({emitEvent:!1})}updateMessageTypeValue(e){"Custom"!==e?.name?this.messageTypeFormGroup.get("messageType").disable({emitEvent:!1}):this.messageTypeFormGroup.get("messageType").enable({emitEvent:!1}),this.messageTypeFormGroup.get("messageType").patchValue(e.value??null)}static{this.ɵfac=function(e){return new(e||Ha)(t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Ha,selectors:[["tb-output-message-type-autocomplete"]],inputs:{subscriptSizing:"subscriptSizing",disabled:"disabled",required:"required"},features:[t.ɵɵProvidersFeature([{provide:V,useExisting:r((()=>Ha)),multi:!0},{provide:O,useExisting:r((()=>Ha)),multi:!0}])],decls:15,vars:14,consts:[[1,"tb-form-row","no-border","no-padding","tb-standard-fields","column-xs",3,"formGroup"],["hideRequiredMarker","",1,"flex",3,"subscriptSizing"],["formControlName","messageTypeAlias"],[3,"value",4,"ngFor","ngForOf"],[1,"flex",3,"subscriptSizing","hideRequiredMarker"],["matInput","","type","text","formControlName","messageType"],["type","button","matSuffix","","mat-icon-button","","aria-label","Copy","ngxClipboard","",3,"cbContent","matTooltip",4,"ngIf"],[4,"ngIf"],[3,"value"],["type","button","matSuffix","","mat-icon-button","","aria-label","Copy","ngxClipboard","",3,"cbContent","matTooltip"],["aria-hidden","false","aria-label","help-icon"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"mat-form-field",1)(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-select",2),t.ɵɵtemplate(6,ja,2,2,"mat-option",3),t.ɵɵelementEnd()(),t.ɵɵelementStart(7,"mat-form-field",4)(8,"mat-label"),t.ɵɵtext(9),t.ɵɵpipe(10,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(11,"input",5),t.ɵɵtemplate(12,Ga,4,4,"button",6)(13,Ka,3,3,"mat-error",7)(14,Ua,3,3,"mat-error",7),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.messageTypeFormGroup),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("subscriptSizing",n.subscriptSizing),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,10,"tb.rulenode.output-message-type")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.messageTypes),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("subscriptSizing",n.subscriptSizing),t.ɵɵproperty("hideRequiredMarker",n.messageTypeFormGroup.get("messageType").disabled),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(10,12,"tb.rulenode.message-type-value")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.messageTypeFormGroup.get("messageType").value),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.messageTypeFormGroup.get("messageType").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.messageTypeFormGroup.get("messageType").hasError("maxlength")))},dependencies:t.ɵɵgetComponentDepsFactory(Ha),encapsulation:2})}}e("OutputMessageTypeAutocompleteComponent",Ha),J([h()],Ha.prototype,"disabled",void 0),J([h()],Ha.prototype,"required",null);const za=(e,t)=>({keyText:e,valText:t});function $a(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,e.keyRequiredText)," ")}}function Qa(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,e.valRequiredText)," ")}}function Ja(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",10)(1,"mat-form-field",11),t.ɵɵelement(2,"input",12),t.ɵɵpipe(3,"translate"),t.ɵɵtemplate(4,$a,3,3,"mat-error",13),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field",11),t.ɵɵelement(6,"input",12),t.ɵɵpipe(7,"translate"),t.ɵɵtemplate(8,Qa,3,3,"mat-error",13),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"button",14),t.ɵɵpipe(10,"translate"),t.ɵɵpipe(11,"async"),t.ɵɵlistener("click",(function(){const n=t.ɵɵrestoreView(e).index,r=t.ɵɵnextContext();return t.ɵɵresetView(r.removeKeyVal(n))})),t.ɵɵelementStart(12,"mat-icon"),t.ɵɵtext(13,"close"),t.ɵɵelementEnd()()()}if(2&e){const e=n.$implicit,r=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(3,10,r.keyText)),t.ɵɵproperty("formControl",e.get("key")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("key").hasError("required")),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(7,12,r.valText)),t.ɵɵproperty("formControl",e.get("value")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.get("value").hasError("required")),t.ɵɵadvance(),t.ɵɵclassProp("!hidden",r.disabled),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(10,14,"tb.key-val.remove-entry")),t.ɵɵproperty("disabled",t.ɵɵpipeBind1(11,16,r.isLoading$))}}function Ya(e,n){if(1&e&&(t.ɵɵelement(0,"div",15),t.ɵɵpipe(1,"translate"),t.ɵɵpipe(2,"safe")),2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("innerHTML",t.ɵɵpipeBind2(2,3,t.ɵɵpipeBind1(1,1,e.hintText),"html"),t.ɵɵsanitizeHtml)}}class Wa extends y{get required(){return this.requiredValue}set required(e){this.requiredValue=Z(e)}constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.injector=n,this.fb=r,this.propagateChange=null,this.valueChangeSubscription=null}ngOnInit(){this.ngControl=this.injector.get(B),null!=this.ngControl&&(this.ngControl.valueAccessor=this),this.kvListFormGroup=this.fb.group({}),this.kvListFormGroup.addControl("keyVals",this.fb.array([]))}keyValsFormArray(){return this.kvListFormGroup.get("keyVals")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.kvListFormGroup.disable({emitEvent:!1}):this.kvListFormGroup.enable({emitEvent:!1})}writeValue(e){this.valueChangeSubscription&&this.valueChangeSubscription.unsubscribe();const t=[];if(e)for(const n of Object.keys(e))Object.prototype.hasOwnProperty.call(e,n)&&t.push(this.fb.group({key:[n,[N.required]],value:[e[n],[N.required]]}));this.kvListFormGroup.setControl("keyVals",this.fb.array(t)),this.valueChangeSubscription=this.kvListFormGroup.valueChanges.subscribe((()=>{this.updateModel()}))}removeKeyVal(e){this.kvListFormGroup.get("keyVals").removeAt(e)}addKeyVal(){this.kvListFormGroup.get("keyVals").push(this.fb.group({key:["",[N.required]],value:["",[N.required]]}))}validate(e){const t=this.kvListFormGroup.get("keyVals").value;if(!t.length&&this.required)return{kvMapRequired:!0};if(!this.kvListFormGroup.valid)return{kvFieldsRequired:!0};if(this.uniqueKeyValuePairValidator)for(const e of t)if(e.key===e.value)return{uniqueKeyValuePair:!0};return null}updateModel(){const e=this.kvListFormGroup.get("keyVals").value;if(this.required&&!e.length||!this.kvListFormGroup.valid)this.propagateChange(null);else{const t={};e.forEach((e=>{t[e.key]=e.value})),this.propagateChange(t)}}static{this.ɵfac=function(e){return new(e||Wa)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(K.TranslateService),t.ɵɵdirectiveInject(t.Injector),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Wa,selectors:[["tb-kv-map-config-old"]],inputs:{disabled:"disabled",uniqueKeyValuePairValidator:"uniqueKeyValuePairValidator",requiredText:"requiredText",keyText:"keyText",keyRequiredText:"keyRequiredText",valText:"valText",valRequiredText:"valRequiredText",hintText:"hintText",required:"required"},features:[t.ɵɵProvidersFeature([{provide:V,useExisting:r((()=>Wa)),multi:!0},{provide:O,useExisting:r((()=>Wa)),multi:!0}]),t.ɵɵInheritDefinitionFeature],decls:21,vars:26,consts:[[1,"tb-kv-map-config","flex","flex-col",3,"formGroup"],[1,"header","flex","flex-1","flex-row","gap-2"],[1,"cell","tb-required","flex-1"],["innerHTML",t.ɵɵtrustConstantHtml` `,2,"width","52px"],[1,"body"],["class","row flex flex-row items-center justify-start gap-2","formArrayName","keyVals",4,"ngFor","ngForOf"],["class","tb-hint",3,"innerHTML",4,"ngIf"],[3,"error"],[2,"margin-top","16px"],["mat-button","","mat-raised-button","","color","primary","type","button","matTooltipPosition","above",3,"click","disabled","matTooltip"],["formArrayName","keyVals",1,"row","flex","flex-row","items-center","justify-start","gap-2"],[1,"cell","mat-block","flex-1"],["matInput","","required","",3,"formControl","placeholder"],[4,"ngIf"],["mat-icon-button","","color","primary","type","button","matTooltipPosition","above",3,"click","disabled","matTooltip"],[1,"tb-hint",3,"innerHTML"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1)(2,"span",2),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"span",2),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(8,"span",3),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"div",4),t.ɵɵtemplate(10,Ja,14,18,"div",5)(11,Ya,3,6,"div",6),t.ɵɵelementEnd(),t.ɵɵelement(12,"tb-error",7),t.ɵɵelementStart(13,"div",8)(14,"button",9),t.ɵɵpipe(15,"translate"),t.ɵɵpipe(16,"async"),t.ɵɵlistener("click",(function(){return n.addKeyVal()})),t.ɵɵelementStart(17,"mat-icon"),t.ɵɵtext(18,"add"),t.ɵɵelementEnd(),t.ɵɵtext(19),t.ɵɵpipe(20,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.kvListFormGroup),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,13,n.keyText)),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(7,15,n.valText)),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",n.disabled),t.ɵɵadvance(2),t.ɵɵproperty("ngForOf",n.keyValsFormArray().controls),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.hintText),t.ɵɵadvance(),t.ɵɵproperty("error",n.ngControl.hasError("kvMapRequired")||n.ngControl.hasError("uniqueKeyValuePair")?n.ngControl.hasError("kvMapRequired")?n.translate.instant(n.requiredText):n.translate.instant("tb.key-val.unique-key-value-pair-error",t.ɵɵpureFunction2(23,za,n.translate.instant(n.keyText),n.translate.instant(n.valText))):""),t.ɵɵadvance(2),t.ɵɵclassProp("!hidden",n.disabled),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(15,17,"tb.key-val.add-entry")),t.ɵɵproperty("disabled",t.ɵɵpipeBind1(16,19,n.isLoading$)),t.ɵɵadvance(5),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(20,21,"action.add")," "))},dependencies:t.ɵɵgetComponentDepsFactory(Wa),styles:["[_nghost-%COMP%] .tb-kv-map-config[_ngcontent-%COMP%]{margin-bottom:16px}[_nghost-%COMP%] .tb-kv-map-config[_ngcontent-%COMP%] .header[_ngcontent-%COMP%]{padding-left:5px;padding-right:5px;padding-bottom:5px}[_nghost-%COMP%] .tb-kv-map-config[_ngcontent-%COMP%] .header[_ngcontent-%COMP%] .cell[_ngcontent-%COMP%]{padding-left:5px;padding-right:5px;color:#757575;font-size:12px;font-weight:700;white-space:nowrap}[_nghost-%COMP%] .tb-kv-map-config[_ngcontent-%COMP%] .header[_ngcontent-%COMP%] .tb-required[_ngcontent-%COMP%]:after{color:#757575;font-size:12px;font-weight:700}[_nghost-%COMP%] .tb-kv-map-config[_ngcontent-%COMP%] .body[_ngcontent-%COMP%]{padding-left:5px;padding-right:5px;padding-bottom:0;max-height:300px;overflow:auto}[_nghost-%COMP%] .tb-kv-map-config[_ngcontent-%COMP%] .body[_ngcontent-%COMP%] .cell[_ngcontent-%COMP%]{padding-left:5px;padding-right:5px}[_nghost-%COMP%] .tb-kv-map-config[_ngcontent-%COMP%] tb-error[_ngcontent-%COMP%]{display:block;margin-top:-12px}"]})}}function Xa(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-chip-option",4),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext();t.ɵɵpropertyInterpolate("selectable",r.chipControlGroup.get("chipControl").value!==e.value),t.ɵɵproperty("value",e.value),t.ɵɵadvance(),t.ɵɵtextInterpolate(e.name)}}e("KvMapConfigOldComponent",Wa);class Za{constructor(e,t){this.fb=e,this.translate=t,this.translation=Ht,this.propagateChange=()=>{},this.destroy$=new Y,this.selectOptions=[]}ngOnInit(){this.initOptions(),this.chipControlGroup=this.fb.group({chipControl:[null,[]]}),this.chipControlGroup.get("chipControl").valueChanges.pipe(ie(this.destroy$)).subscribe((e=>{e&&this.propagateChange(e)}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}initOptions(){for(const e of this.translation.keys())this.selectOptions.push({value:e,name:this.translate.instant(this.translation.get(e))})}writeValue(e){this.chipControlGroup.get("chipControl").patchValue(e,{emitEvent:!1})}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){e?this.chipControlGroup.disable({emitEvent:!1}):this.chipControlGroup.enable({emitEvent:!1})}static{this.ɵfac=function(e){return new(e||Za)(t.ɵɵdirectiveInject(k.FormBuilder),t.ɵɵdirectiveInject(K.TranslateService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Za,selectors:[["tb-msg-metadata-chip"]],inputs:{labelText:"labelText",translation:"translation"},features:[t.ɵɵProvidersFeature([{provide:V,useExisting:r((()=>Za)),multi:!0}])],decls:5,vars:3,consts:[[1,"tb-form-row","space-between",3,"formGroup"],[1,"fixed-title-width"],["formControlName","chipControl"],["color","primary",3,"selectable","value",4,"ngFor","ngForOf"],["color","primary",3,"selectable","value"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0)(1,"div",1),t.ɵɵtext(2),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-chip-listbox",2),t.ɵɵtemplate(4,Xa,2,3,"mat-chip-option",3),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.chipControlGroup),t.ɵɵadvance(2),t.ɵɵtextInterpolate(n.labelText),t.ɵɵadvance(2),t.ɵɵproperty("ngForOf",n.selectOptions))},dependencies:t.ɵɵgetComponentDepsFactory(Za),encapsulation:2})}}function ei(e,n){1&e&&(t.ɵɵelementStart(0,"div",13),t.ɵɵtext(1," tb.rulenode.map-fields-required "),t.ɵɵelementEnd())}function ti(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",13),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.requiredText," ")}}function ni(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",21),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e.value),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.name," ")}}function ri(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",14)(1,"mat-form-field",15)(2,"mat-select",16),t.ɵɵtemplate(3,ni,2,2,"mat-option",17),t.ɵɵelementEnd()(),t.ɵɵelementStart(4,"mat-form-field",15),t.ɵɵelement(5,"input",18),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"div",19)(7,"button",20),t.ɵɵpipe(8,"translate"),t.ɵɵpipe(9,"async"),t.ɵɵlistener("click",(function(){const n=t.ɵɵrestoreView(e).index,r=t.ɵɵnextContext();return t.ɵɵresetView(r.removeKeyVal(n))})),t.ɵɵelementStart(10,"mat-icon"),t.ɵɵtext(11,"delete"),t.ɵɵelementEnd()()()()}if(2&e){const e=n.$implicit,r=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵproperty("placeholder",r.selectText)("formControl",e.get("key")),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",r.filterSelectOptions(e)),t.ɵɵadvance(2),t.ɵɵproperty("placeholder",r.valText)("formControl",e.get("value")),t.ɵɵadvance(2),t.ɵɵclassProp("tb-hidden",1===r.keyValsFormArray().controls.length),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(8,9,"tb.key-val.remove-mapping-entry")),t.ɵɵproperty("disabled",t.ɵɵpipeBind1(9,11,r.isLoading$))}}e("MsgMetadataChipComponent",Za);class ai extends y{constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.injector=n,this.fb=r,this.destroy$=new Y,this.sourceFieldSubcritption=[],this.propagateChange=null,this.disabled=!1,this.required=!1,this.oneMapRequiredValidator=e=>e.get("keyVals").value.length,this.propagateNestedErrors=e=>{if(this.svListFormGroup&&this.svListFormGroup.get("keyVals")&&"VALID"===this.svListFormGroup.get("keyVals")?.status)return null;const t={};if(this.svListFormGroup&&this.svListFormGroup.setErrors(null),e instanceof w||e instanceof M){if(e.errors)for(const n of Object.keys(e.errors))t[n]=!0;for(const n of Object.keys(e.controls)){const r=this.propagateNestedErrors(e.controls[n]);if(r&&Object.keys(r).length)for(const e of Object.keys(r))t[e]=!0}return t}if(e.errors)for(const n of Object.keys(e.errors))t[n]=!0;return R(t,{})?null:t}}ngOnInit(){this.ngControl=this.injector.get(B),null!=this.ngControl&&(this.ngControl.valueAccessor=this),this.svListFormGroup=this.fb.group({keyVals:this.fb.array([])},{validators:[this.propagateNestedErrors,this.oneMapRequiredValidator]}),this.svListFormGroup.valueChanges.pipe(ie(this.destroy$)).subscribe((()=>{this.updateModel()}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}keyValsFormArray(){return this.svListFormGroup.get("keyVals")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.svListFormGroup.disable({emitEvent:!1}):this.svListFormGroup.enable({emitEvent:!1})}writeValue(e){const t=Object.keys(e).map((t=>({key:t,value:e[t]})));if(this.keyValsFormArray().length===t.length)this.keyValsFormArray().patchValue(t,{emitEvent:!1});else{const e=[];t.forEach((t=>{e.push(this.fb.group({key:[t.key,[N.required]],value:[t.value,[N.required,N.pattern(/(?:.|\s)*\S(&:.|\s)*/)]]}))})),this.svListFormGroup.setControl("keyVals",this.fb.array(e,this.propagateNestedErrors),{emitEvent:!1});for(const e of this.keyValsFormArray().controls)this.keyChangeSubscribe(e)}}filterSelectOptions(e){const t=[];for(const e of this.svListFormGroup.get("keyVals").value){const n=this.selectOptions.find((t=>t.value===e.key));n&&t.push(n)}const n=[];for(const r of this.selectOptions)P(t.find((e=>e.value===r.value)))&&r.value!==e?.get("key").value||n.push(r);return n}removeKeyVal(e){this.keyValsFormArray().removeAt(e),this.sourceFieldSubcritption[e].unsubscribe(),this.sourceFieldSubcritption.splice(e,1)}addKeyVal(){this.keyValsFormArray().push(this.fb.group({key:["",[N.required]],value:["",[N.required,N.pattern(/(?:.|\s)*\S(&:.|\s)*/)]]})),this.keyChangeSubscribe(this.keyValsFormArray().at(this.keyValsFormArray().length-1))}keyChangeSubscribe(e){this.sourceFieldSubcritption.push(e.get("key").valueChanges.pipe(ie(this.destroy$)).subscribe((t=>{const n=ct.get(t);e.get("value").patchValue(this.targetKeyPrefix+n[0].toUpperCase()+n.slice(1))})))}validate(e){return!this.svListFormGroup.get("keyVals").value.length&&this.required?{svMapRequired:!0}:this.svListFormGroup.valid?null:{svFieldsRequired:!0}}updateModel(){const e=this.svListFormGroup.get("keyVals").value;if(this.required&&!e.length||!this.svListFormGroup.valid)this.propagateChange(null);else{const t={};e.forEach((e=>{t[e.key]=e.value})),this.propagateChange(t)}}static{this.ɵfac=function(e){return new(e||ai)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(K.TranslateService),t.ɵɵdirectiveInject(t.Injector),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:ai,selectors:[["tb-sv-map-config"]],inputs:{selectOptions:"selectOptions",disabled:"disabled",labelText:"labelText",requiredText:"requiredText",targetKeyPrefix:"targetKeyPrefix",selectText:"selectText",selectRequiredText:"selectRequiredText",valText:"valText",valRequiredText:"valRequiredText",hintText:"hintText",popupHelpLink:"popupHelpLink",required:"required"},features:[t.ɵɵProvidersFeature([{provide:V,useExisting:r((()=>ai)),multi:!0},{provide:O,useExisting:r((()=>ai)),multi:!0}]),t.ɵɵInheritDefinitionFeature],decls:22,vars:15,consts:[[1,"tb-form-panel","stroked",3,"formGroup"],[1,"tb-form-row","no-padding","no-border","space-between"],[1,"tb-form-panel-title"],["class","tb-form-panel-hint tb-error","translate","",4,"ngIf"],[1,"tb-form-panel","no-border","no-padding"],[1,"tb-form-table"],[1,"tb-form-table-header"],[1,"tb-form-table-header-cell","field-space"],[1,"tb-form-table-header-cell","actions-header"],[1,"tb-form-table-body"],["class","tb-form-table-row",4,"ngFor","ngForOf"],["type","button","mat-stroked-button","","color","primary",3,"click","disabled"],[3,"hintText","popupHelpLink"],["translate","",1,"tb-form-panel-hint","tb-error"],[1,"tb-form-table-row"],["appearance","outline","subscriptSizing","dynamic",1,"tb-inline-field","field-space"],["required","",3,"placeholder","formControl"],[3,"value",4,"ngFor","ngForOf"],["matInput","",3,"placeholder","formControl"],[1,"tb-form-table-row-cell-buttons"],["type","button","mat-icon-button","","matTooltipPosition","above",3,"click","disabled","matTooltip"],[3,"value"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3),t.ɵɵelementEnd(),t.ɵɵtemplate(4,ei,2,0,"div",3)(5,ti,2,1,"div",3),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"div",4)(7,"div",5)(8,"div",6)(9,"div",7),t.ɵɵtext(10),t.ɵɵelementEnd(),t.ɵɵelementStart(11,"div",7),t.ɵɵtext(12),t.ɵɵelementEnd(),t.ɵɵelement(13,"div",8),t.ɵɵelementEnd(),t.ɵɵelementStart(14,"div",9),t.ɵɵtemplate(15,ri,12,13,"div",10),t.ɵɵelementEnd()()(),t.ɵɵelementStart(16,"div")(17,"button",11),t.ɵɵpipe(18,"async"),t.ɵɵlistener("click",(function(){return n.addKeyVal()})),t.ɵɵtext(19),t.ɵɵpipe(20,"translate"),t.ɵɵelementEnd()(),t.ɵɵelement(21,"tb-example-hint",12),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.svListFormGroup),t.ɵɵadvance(3),t.ɵɵtextInterpolate(n.labelText),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.svListFormGroup.hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.svListFormGroup.hasError("svMapRequired")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(n.selectText),t.ɵɵadvance(2),t.ɵɵtextInterpolate(n.valText),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.keyValsFormArray().controls),t.ɵɵadvance(2),t.ɵɵproperty("disabled",t.ɵɵpipeBind1(18,11,n.isLoading$)||n.keyValsFormArray().length>=n.selectOptions.length),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(20,13,"tb.key-val.add-mapping-entry")," "),t.ɵɵadvance(2),t.ɵɵproperty("hintText",n.hintText)("popupHelpLink",n.popupHelpLink))},dependencies:t.ɵɵgetComponentDepsFactory(ai),styles:["[_nghost-%COMP%] .field-space[_ngcontent-%COMP%]{flex:1 1 50%}[_nghost-%COMP%] .actions-header[_ngcontent-%COMP%]{width:40px}"]})}}function ii(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",11),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.directionTypeTranslations.get(e))," ")}}e("SvMapConfigComponent",ai),J([h()],ai.prototype,"disabled",void 0),J([h()],ai.prototype,"required",void 0);class oi extends y{get required(){return this.requiredValue}set required(e){this.requiredValue=Z(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(d),this.directionTypeTranslations=b,this.propagateChange=null}ngOnInit(){this.relationsQueryFormGroup=this.fb.group({fetchLastLevelOnly:[!1,[]],direction:[null,[N.required]],maxLevel:[null,[]],filters:[null]}),this.relationsQueryFormGroup.valueChanges.subscribe((e=>{this.relationsQueryFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.relationsQueryFormGroup.disable({emitEvent:!1}):this.relationsQueryFormGroup.enable({emitEvent:!1})}writeValue(e){this.relationsQueryFormGroup.reset(e||{},{emitEvent:!1})}static{this.ɵfac=function(e){return new(e||oi)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:oi,selectors:[["tb-relations-query-config-old"]],inputs:{disabled:"disabled",required:"required"},features:[t.ɵɵProvidersFeature([{provide:V,useExisting:r((()=>oi)),multi:!0}]),t.ɵɵInheritDefinitionFeature],decls:18,vars:8,consts:[[1,"flex","flex-col",3,"formGroup"],["formControlName","fetchLastLevelOnly"],[1,"flex","flex-row","gap-2"],[1,"mat-block",2,"min-width","100px"],["translate",""],["required","","matInput","","formControlName","direction"],[3,"value",4,"ngFor","ngForOf"],["floatLabel","always",1,"mat-block","flex-1"],["matInput","","type","number","min","1","step","1","formControlName","maxLevel",3,"placeholder"],["translate","",1,"mat-caption",2,"color","#6e6e6e"],["formControlName","filters"],[3,"value"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"mat-checkbox",1),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",2)(5,"mat-form-field",3)(6,"mat-label",4),t.ɵɵtext(7,"relation.direction"),t.ɵɵelementEnd(),t.ɵɵelementStart(8,"mat-select",5),t.ɵɵtemplate(9,ii,3,4,"mat-option",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(10,"mat-form-field",7)(11,"mat-label",4),t.ɵɵtext(12,"tb.rulenode.max-relation-level"),t.ɵɵelementEnd(),t.ɵɵelement(13,"input",8),t.ɵɵpipe(14,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(15,"div",9),t.ɵɵtext(16,"relation.relation-filters"),t.ɵɵelementEnd(),t.ɵɵelement(17,"tb-relation-filters",10),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.relationsQueryFormGroup),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,4,"alias.last-level-relation")," "),t.ɵɵadvance(7),t.ɵɵproperty("ngForOf",n.directionTypes),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(14,6,"tb.rulenode.unlimited-level")))},dependencies:t.ɵɵgetComponentDepsFactory(oi),encapsulation:2})}}e("RelationsQueryConfigOldComponent",oi);const li=e=>({latestTsKeyName:e}),si=e=>({inputName:e});function pi(e,n){1&e&&t.ɵɵelementContainer(0,9)}function mi(e,n){1&e&&t.ɵɵelementContainer(0,9)}function di(e,n){1&e&&t.ɵɵelementContainer(0,9)}function ui(e,n){1&e&&t.ɵɵelementContainer(0,9)}function ci(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",10),t.ɵɵpipe(1,"translate"),t.ɵɵelementStart(2,"mat-slide-toggle",11),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind2(1,2,"tb.rulenode.fetch-latest-telemetry-with-timestamp-tooltip",t.ɵɵpureFunction1(7,li,e.attributeControlGroup.get("latestTsKeyNames").value[0]))),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,5,"tb.rulenode.fetch-latest-telemetry-with-timestamp")," ")}}function fi(e,n){1&e&&(t.ɵɵelementStart(0,"mat-icon",12),t.ɵɵpipe(1,"translate"),t.ɵɵpipe(2,"translate"),t.ɵɵtext(3,"help"),t.ɵɵelementEnd()),2&e&&t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind2(2,3,"tb.rulenode.chip-help",t.ɵɵpureFunction1(6,si,t.ɵɵpipeBind1(1,1,"tb.rulenode.field-name"))))}class gi{constructor(e,t){this.translate=e,this.fb=t,this.propagateChange=e=>{},this.destroy$=new Y,this.separatorKeysCodes=[U,H,z],this.onTouched=()=>{}}ngOnInit(){this.attributeControlGroup=this.fb.group({clientAttributeNames:[[],[]],sharedAttributeNames:[[],[]],serverAttributeNames:[[],[]],latestTsKeyNames:[[],[]],getLatestValueWithTs:[!1,[]]},{validators:this.atLeastOne(N.required,["clientAttributeNames","sharedAttributeNames","serverAttributeNames","latestTsKeyNames"])}),this.attributeControlGroup.valueChanges.pipe(ie(this.destroy$)).subscribe((e=>{this.propagateChange(this.preparePropagateValue(e))}))}preparePropagateValue(e){const t={};for(const n in e)t[n]="getLatestValueWithTs"===n||P(e[n])?e[n]:[];return t}validate(){return this.attributeControlGroup.valid?null:{atLeastOneRequired:!0}}atLeastOne(e,t=null){return n=>{t||(t=Object.keys(n.controls));return n?.controls&&t.some((t=>!e(n.controls[t])))?null:{atLeastOne:!0}}}writeValue(e){this.attributeControlGroup.setValue(e,{emitEvent:!1})}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){this.onTouched=e}setDisabledState(e){e?this.attributeControlGroup.disable({emitEvent:!1}):this.attributeControlGroup.enable({emitEvent:!1})}ngOnDestroy(){this.destroy$.next(null),this.destroy$.complete()}static{this.ɵfac=function(e){return new(e||gi)(t.ɵɵdirectiveInject(K.TranslateService),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:gi,selectors:[["tb-select-attributes"]],inputs:{popupHelpLink:"popupHelpLink"},features:[t.ɵɵProvidersFeature([{provide:V,useExisting:r((()=>gi)),multi:!0},{provide:O,useExisting:gi,multi:!0}])],decls:22,vars:34,consts:[["helpIcon",""],[1,"tb-form-panel","no-padding","no-border",3,"formGroup"],[3,"hintText","popupHelpLink"],["subscriptSizing","dynamic","editable","","formControlName","clientAttributeNames",1,"mat-block",3,"focusout","placeholder","label"],["matSuffix","",4,"ngTemplateOutlet"],["subscriptSizing","dynamic","editable","","formControlName","sharedAttributeNames",1,"mat-block",3,"focusout","placeholder","label"],["subscriptSizing","dynamic","editable","","formControlName","serverAttributeNames",1,"mat-block",3,"focusout","placeholder","label"],["subscriptSizing","dynamic","editable","","formControlName","latestTsKeyNames",1,"mat-block",3,"focusout","placeholder","label"],["class","tb-form-row no-border no-padding",3,"tb-hint-tooltip-icon",4,"ngIf"],["matSuffix",""],[1,"tb-form-row","no-border","no-padding",3,"tb-hint-tooltip-icon"],["formControlName","getLatestValueWithTs",1,"mat-slide"],["aria-hidden","false","aria-label","help-icon","color","primary",1,"help-icon","margin-8","cursor-pointer",3,"matTooltip"]],template:function(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"div",1),t.ɵɵelement(1,"tb-example-hint",2),t.ɵɵpipe(2,"translate"),t.ɵɵelementStart(3,"tb-string-items-list",3),t.ɵɵpipe(4,"translate"),t.ɵɵpipe(5,"translate"),t.ɵɵlistener("focusout",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.onTouched())})),t.ɵɵtemplate(6,pi,1,0,"ng-container",4),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"tb-string-items-list",5),t.ɵɵpipe(8,"translate"),t.ɵɵpipe(9,"translate"),t.ɵɵlistener("focusout",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.onTouched())})),t.ɵɵtemplate(10,mi,1,0,"ng-container",4),t.ɵɵelementEnd(),t.ɵɵelementStart(11,"tb-string-items-list",6),t.ɵɵpipe(12,"translate"),t.ɵɵpipe(13,"translate"),t.ɵɵlistener("focusout",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.onTouched())})),t.ɵɵtemplate(14,di,1,0,"ng-container",4),t.ɵɵelementEnd(),t.ɵɵelementStart(15,"tb-string-items-list",7),t.ɵɵpipe(16,"translate"),t.ɵɵpipe(17,"translate"),t.ɵɵlistener("focusout",(function(){return t.ɵɵrestoreView(e),t.ɵɵresetView(n.onTouched())})),t.ɵɵtemplate(18,ui,1,0,"ng-container",4),t.ɵɵelementEnd(),t.ɵɵtemplate(19,ci,5,9,"div",8),t.ɵɵelementEnd(),t.ɵɵtemplate(20,fi,4,8,"ng-template",null,0,t.ɵɵtemplateRefExtractor)}if(2&e){let e;const r=t.ɵɵreference(21);t.ɵɵproperty("formGroup",n.attributeControlGroup),t.ɵɵadvance(),t.ɵɵproperty("hintText",t.ɵɵpipeBind1(2,16,"tb.rulenode.kv-map-pattern-hint"))("popupHelpLink",n.popupHelpLink),t.ɵɵadvance(2),t.ɵɵproperty("placeholder",t.ɵɵpipeBind1(4,18,"tb.rulenode.add-attribute-key"))("label",t.ɵɵpipeBind1(5,20,"tb.rulenode.client-attributes")),t.ɵɵadvance(3),t.ɵɵproperty("ngTemplateOutlet",r),t.ɵɵadvance(),t.ɵɵproperty("placeholder",t.ɵɵpipeBind1(8,22,"tb.rulenode.add-attribute-key"))("label",t.ɵɵpipeBind1(9,24,"tb.rulenode.shared-attributes")),t.ɵɵadvance(3),t.ɵɵproperty("ngTemplateOutlet",r),t.ɵɵadvance(),t.ɵɵproperty("placeholder",t.ɵɵpipeBind1(12,26,"tb.rulenode.add-attribute-key"))("label",t.ɵɵpipeBind1(13,28,"tb.rulenode.server-attributes")),t.ɵɵadvance(3),t.ɵɵproperty("ngTemplateOutlet",r),t.ɵɵadvance(),t.ɵɵproperty("placeholder",t.ɵɵpipeBind1(16,30,"tb.rulenode.add-telemetry-key"))("label",t.ɵɵpipeBind1(17,32,"tb.rulenode.latest-telemetry")),t.ɵɵadvance(3),t.ɵɵproperty("ngTemplateOutlet",r),t.ɵɵadvance(),t.ɵɵproperty("ngIf",(null==(e=n.attributeControlGroup.get("latestTsKeyNames").value)?null:e.length)>0)}},dependencies:t.ɵɵgetComponentDepsFactory(gi),encapsulation:2})}}e("SelectAttributesComponent",gi);class hi extends y{constructor(e,t){super(e),this.store=e,this.fb=t,this.propagateChange=null,this.destroy$=new Y,this.alarmStatus=x,this.alarmStatusTranslations=C}ngOnInit(){this.alarmStatusGroup=this.fb.group({alarmStatus:[null,[]]}),this.alarmStatusGroup.get("alarmStatus").valueChanges.pipe(ie(this.destroy$)).subscribe((e=>{this.propagateChange(e)}))}setDisabledState(e){e?this.alarmStatusGroup.disable({emitEvent:!1}):this.alarmStatusGroup.enable({emitEvent:!1})}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}writeValue(e){this.alarmStatusGroup.get("alarmStatus").patchValue(e,{emitEvent:!1})}static{this.ɵfac=function(e){return new(e||hi)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:hi,selectors:[["tb-alarm-status-select"]],features:[t.ɵɵProvidersFeature([{provide:V,useExisting:r((()=>hi)),multi:!0}]),t.ɵɵInheritDefinitionFeature],decls:16,vars:17,consts:[[1,"flex","flex-col","items-center","justify-center",3,"formGroup"],["multiple","","formControlName","alarmStatus",1,"chip-listbox","flex","flex-col"],[1,"toggle-column"],[1,"option","flex-1",3,"value"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"mat-chip-listbox",1)(2,"div",2)(3,"mat-chip-option",3),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"mat-chip-option",3),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(9,"div",2)(10,"mat-chip-option",3),t.ɵɵtext(11),t.ɵɵpipe(12,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"mat-chip-option",3),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd()()()()),2&e&&(t.ɵɵproperty("formGroup",n.alarmStatusGroup),t.ɵɵadvance(3),t.ɵɵproperty("value",n.alarmStatus.ACTIVE_UNACK),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(5,9,n.alarmStatusTranslations.get(n.alarmStatus.ACTIVE_UNACK))," "),t.ɵɵadvance(2),t.ɵɵproperty("value",n.alarmStatus.ACTIVE_ACK),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(8,11,n.alarmStatusTranslations.get(n.alarmStatus.ACTIVE_ACK))," "),t.ɵɵadvance(3),t.ɵɵproperty("value",n.alarmStatus.CLEARED_UNACK),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(12,13,n.alarmStatusTranslations.get(n.alarmStatus.CLEARED_UNACK))," "),t.ɵɵadvance(2),t.ɵɵproperty("value",n.alarmStatus.CLEARED_ACK),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(15,15,n.alarmStatusTranslations.get(n.alarmStatus.CLEARED_ACK))," "))},dependencies:t.ɵɵgetComponentDepsFactory(hi),styles:["[_nghost-%COMP%] .chip-listbox[_ngcontent-%COMP%]{max-width:460px;width:100%}[_nghost-%COMP%] .chip-listbox[_ngcontent-%COMP%] .toggle-column[_ngcontent-%COMP%]{display:flex;flex:1 1 100%;gap:8px}[_nghost-%COMP%] .chip-listbox[_ngcontent-%COMP%] .option[_ngcontent-%COMP%]{margin:0}@media screen and (max-width: 959px){[_nghost-%COMP%] .chip-listbox[_ngcontent-%COMP%]{max-width:360px}[_nghost-%COMP%] .chip-listbox[_ngcontent-%COMP%] .toggle-column[_ngcontent-%COMP%]{flex-direction:column}}[_nghost-%COMP%] .chip-listbox .mdc-evolution-chip-set__chips{gap:8px}[_nghost-%COMP%] .chip-listbox .option button{flex-basis:100%;justify-content:start}[_nghost-%COMP%] .chip-listbox .option .mdc-evolution-chip__graphic{flex-grow:0}"]})}}e("AlarmStatusSelectComponent",hi);const yi=()=>({maxWidth:"820px"});function bi(e,n){if(1&e&&(t.ɵɵelement(0,"div",3),t.ɵɵpipe(1,"translate")),2&e){const e=t.ɵɵnextContext();t.ɵɵpropertyInterpolate("tb-help-popup",e.popupHelpLink),t.ɵɵpropertyInterpolate("trigger-text",t.ɵɵpipeBind1(1,3,"tb.key-val.see-examples")),t.ɵɵproperty("tb-help-popup-style",t.ɵɵpureFunction0(5,yi))}}class vi{constructor(){this.textAlign="left"}static{this.ɵfac=function(e){return new(e||vi)}}static{this.ɵcmp=t.ɵɵdefineComponent({type:vi,selectors:[["tb-example-hint"]],inputs:{hintText:"hintText",popupHelpLink:"popupHelpLink",textAlign:"textAlign"},decls:5,vars:10,consts:[[1,"tb-form-hint","tb-primary-fill","space-between",3,"hidden"],[1,"hint-text",3,"innerHTML"],["class","see-example","hintMode","","tb-help-popup-placement","right","trigger-style","letter-spacing:0.25px; font-size:12px",3,"tb-help-popup","tb-help-popup-style","trigger-text",4,"ngIf"],["hintMode","","tb-help-popup-placement","right","trigger-style","letter-spacing:0.25px; font-size:12px",1,"see-example",3,"tb-help-popup","tb-help-popup-style","trigger-text"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"div",0),t.ɵɵelement(1,"div",1),t.ɵɵpipe(2,"translate"),t.ɵɵpipe(3,"safe"),t.ɵɵtemplate(4,bi,2,6,"div",2),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("hidden",!n.hintText),t.ɵɵadvance(),t.ɵɵstyleProp("text-align",n.textAlign),t.ɵɵproperty("innerHTML",t.ɵɵpipeBind2(3,7,t.ɵɵpipeBind1(2,5,n.hintText),"html"),t.ɵɵsanitizeHtml),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.popupHelpLink))},dependencies:t.ɵɵgetComponentDepsFactory(vi),styles:["[_nghost-%COMP%] .space-between[_ngcontent-%COMP%]{display:flex;justify-content:space-between;gap:20px}[_nghost-%COMP%] .space-between[_ngcontent-%COMP%] .see-example[_ngcontent-%COMP%]{display:flex;flex-shrink:0}[_nghost-%COMP%] .hint-text[_ngcontent-%COMP%]{width:100%}"]})}}e("ExampleHintComponent",vi);class xi{static{this.ɵfac=function(e){return new(e||xi)}}static{this.ɵmod=t.ɵɵdefineNgModule({type:xi})}static{this.ɵinj=t.ɵɵdefineInjector({imports:[$,S,Q,_r,zr,Wr,ma,va,Oa,_a,Ha,Wa,Za,ai,oi,gi,hi,vi]})}}e("RulenodeCoreConfigCommonModule",xi),("undefined"==typeof ngJitMode||ngJitMode)&&t.ɵɵsetNgModuleScope(xi,{declarations:[_r,zr,Wr,ma,va,Oa,_a,Ha,Wa,Za,ai,oi,gi,hi,vi],imports:[$,S,Q],exports:[_r,zr,Wr,ma,va,Oa,_a,Ha,Wa,Za,ai,oi,gi,hi,vi]});class Ci{static{this.ɵfac=function(e){return new(e||Ci)}}static{this.ɵmod=t.ɵɵdefineNgModule({type:Ci})}static{this.ɵinj=t.ɵɵdefineInjector({imports:[$,S,Q,xi,Cr,ce,mr,rr,Vn,se,Ce,Re,He,$n,Ye,st,qn,Pn,er,lr,cr,fr,We,Zn,Yn,wr,Br]})}}e("RuleNodeCoreConfigActionModule",Ci),("undefined"==typeof ngJitMode||ngJitMode)&&t.ɵɵsetNgModuleScope(Ci,{declarations:[Cr,ce,mr,rr,Vn,se,Ce,Re,He,$n,Ye,st,qn,Pn,er,lr,cr,fr,We,Zn,Yn,wr,Br],imports:[$,S,Q,xi],exports:[Cr,ce,mr,rr,Vn,se,Ce,Re,He,$n,Ye,st,qn,Pn,er,lr,cr,fr,We,Zn,Yn,wr,Br]});const Si=e=>({inputValueKey:e}),Ti=e=>({periodValueKey:e}),Ii=(e,t)=>({outputValueKey:e,periodValueKey:t}),Ei=e=>({outputValueKey:e});function Fi(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.input-value-key-required")," "))}function qi(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.output-value-key-required")," "))}function Ai(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.number-of-digits-after-floating-point-range")," "))}function ki(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.number-of-digits-after-floating-point-range")," "))}function Ni(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.period-value-key-required")," "))}function wi(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",16)(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",17),t.ɵɵtemplate(5,Ni,3,3,"mat-error",4),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,2,"tb.rulenode.period-value-key")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",e.calculateDeltaConfigForm.get("periodValueKey").hasError("required"))}}class Mi extends i{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n,this.separatorKeysCodes=[U,H,z]}configForm(){return this.calculateDeltaConfigForm}onConfigurationSet(e){this.calculateDeltaConfigForm=this.fb.group({inputValueKey:[e.inputValueKey,[N.required,N.pattern(/(?:.|\s)*\S(&:.|\s)*/)]],outputValueKey:[e.outputValueKey,[N.required,N.pattern(/(?:.|\s)*\S(&:.|\s)*/)]],useCache:[e.useCache,[]],addPeriodBetweenMsgs:[e.addPeriodBetweenMsgs,[]],periodValueKey:[e.periodValueKey,[]],round:[e.round,[N.min(0),N.max(15)]],tellFailureIfDeltaIsNegative:[e.tellFailureIfDeltaIsNegative,[]],excludeZeroDeltas:[e.excludeZeroDeltas,[]]})}prepareInputConfig(e){return{inputValueKey:P(e?.inputValueKey)?e.inputValueKey:null,outputValueKey:P(e?.outputValueKey)?e.outputValueKey:null,useCache:!P(e?.useCache)||e.useCache,addPeriodBetweenMsgs:!!P(e?.addPeriodBetweenMsgs)&&e.addPeriodBetweenMsgs,periodValueKey:P(e?.periodValueKey)?e.periodValueKey:null,round:P(e?.round)?e.round:null,tellFailureIfDeltaIsNegative:!P(e?.tellFailureIfDeltaIsNegative)||e.tellFailureIfDeltaIsNegative,excludeZeroDeltas:!!P(e?.excludeZeroDeltas)&&e.excludeZeroDeltas}}prepareOutputConfig(e){return _(e)}updateValidators(e){this.calculateDeltaConfigForm.get("addPeriodBetweenMsgs").value?this.calculateDeltaConfigForm.get("periodValueKey").setValidators([N.required]):this.calculateDeltaConfigForm.get("periodValueKey").setValidators([]),this.calculateDeltaConfigForm.get("periodValueKey").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["addPeriodBetweenMsgs"]}static{this.ɵfac=function(e){return new(e||Mi)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(K.TranslateService),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Mi,selectors:[["tb-enrichment-node-calculate-delta-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:50,vars:69,consts:[[3,"formGroup"],[1,"gt-sm:flex","gt-sm:flex-row","gt-sm:gap-5.5"],[1,"mat-block","flex-1"],["matInput","","formControlName","inputValueKey"],[4,"ngIf"],["matInput","","formControlName","outputValueKey"],["type","number","min","0","max","15","step","1","matInput","","formControlName","round"],[1,"tb-form-panel","no-padding","no-border"],[1,"tb-form-row","same-padding",3,"tb-hint-tooltip-icon"],["formControlName","tellFailureIfDeltaIsNegative",1,"mat-slide","margin"],["formControlName","useCache",1,"mat-slide","margin"],[1,"tb-form-panel","stroked"],[1,"tb-form-row","no-border","no-padding",3,"tb-hint-tooltip-icon"],["formControlName","addPeriodBetweenMsgs",1,"mat-slide"],["class","mat-block",4,"ngIf"],["formControlName","excludeZeroDeltas",1,"mat-slide","margin"],[1,"mat-block"],["required","","matInput","","formControlName","periodValueKey"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1)(2,"mat-form-field",2)(3,"mat-label"),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(6,"input",3),t.ɵɵtemplate(7,Fi,3,3,"mat-error",4),t.ɵɵelementEnd(),t.ɵɵelementStart(8,"mat-form-field",2)(9,"mat-label"),t.ɵɵtext(10),t.ɵɵpipe(11,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(12,"input",5),t.ɵɵtemplate(13,qi,3,3,"mat-error",4),t.ɵɵelementEnd()(),t.ɵɵelementStart(14,"mat-form-field",2)(15,"mat-label"),t.ɵɵtext(16),t.ɵɵpipe(17,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(18,"input",6),t.ɵɵtemplate(19,Ai,3,3,"mat-error",4)(20,ki,3,3,"mat-error",4),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"div",7)(22,"div",8),t.ɵɵpipe(23,"translate"),t.ɵɵelementStart(24,"mat-slide-toggle",9),t.ɵɵtext(25),t.ɵɵpipe(26,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(27,"div",8),t.ɵɵpipe(28,"translate"),t.ɵɵpipe(29,"translate"),t.ɵɵelementStart(30,"mat-slide-toggle",10),t.ɵɵtext(31),t.ɵɵpipe(32,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(33,"div",11)(34,"div",12),t.ɵɵpipe(35,"translate"),t.ɵɵelementStart(36,"mat-slide-toggle",13),t.ɵɵtext(37),t.ɵɵpipe(38,"translate"),t.ɵɵpipe(39,"translate"),t.ɵɵelementEnd()(),t.ɵɵtemplate(40,wi,6,4,"mat-form-field",14),t.ɵɵelementEnd(),t.ɵɵelementStart(41,"div",8),t.ɵɵpipe(42,"translate"),t.ɵɵpipe(43,"translate"),t.ɵɵpipe(44,"translate"),t.ɵɵpipe(45,"translate"),t.ɵɵpipe(46,"translate"),t.ɵɵelementStart(47,"mat-slide-toggle",15),t.ɵɵtext(48),t.ɵɵpipe(49,"translate"),t.ɵɵelementEnd()()()()),2&e&&(t.ɵɵproperty("formGroup",n.calculateDeltaConfigForm),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(5,19,"tb.rulenode.input-value-key")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.calculateDeltaConfigForm.get("inputValueKey").hasError("required")||n.calculateDeltaConfigForm.get("inputValueKey").hasError("pattern")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(11,21,"tb.rulenode.output-value-key")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.calculateDeltaConfigForm.get("outputValueKey").hasError("required")||n.calculateDeltaConfigForm.get("outputValueKey").hasError("pattern")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(17,23,"tb.rulenode.number-of-digits-after-floating-point")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.calculateDeltaConfigForm.get("round").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.calculateDeltaConfigForm.get("round").hasError("max")),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(23,25,"tb.rulenode.failure-if-delta-negative-tooltip")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(26,27,"tb.rulenode.failure-if-delta-negative")," "),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind2(29,31,"tb.rulenode.use-caching-tooltip",t.ɵɵpureFunction1(58,Si,n.calculateDeltaConfigForm.get("inputValueKey").valid?n.calculateDeltaConfigForm.get("inputValueKey").value:t.ɵɵpipeBind1(28,29,"tb.rulenode.input-value-key")))),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(32,34,"tb.rulenode.use-caching")," "),t.ɵɵadvance(2),t.ɵɵclassProp("no-padding-bottom",n.calculateDeltaConfigForm.get("addPeriodBetweenMsgs").value),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind2(35,36,"tb.rulenode.add-time-difference-between-readings-tooltip",t.ɵɵpureFunction1(60,Ti,n.calculateDeltaConfigForm.get("periodValueKey").valid&&n.calculateDeltaConfigForm.get("addPeriodBetweenMsgs").value?n.calculateDeltaConfigForm.get("periodValueKey").value:"periodInMs"))),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind2(39,41,"tb.rulenode.add-time-difference-between-readings",t.ɵɵpureFunction1(62,Si,n.calculateDeltaConfigForm.get("inputValueKey").valid?n.calculateDeltaConfigForm.get("inputValueKey").value:t.ɵɵpipeBind1(38,39,"tb.rulenode.input-value-key")))," "),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.calculateDeltaConfigForm.get("addPeriodBetweenMsgs").value),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",n.calculateDeltaConfigForm.get("addPeriodBetweenMsgs").value?t.ɵɵpipeBind2(44,48,"tb.rulenode.exclude-zero-deltas-time-difference-hint",t.ɵɵpureFunction2(64,Ii,n.calculateDeltaConfigForm.get("outputValueKey").valid?n.calculateDeltaConfigForm.get("outputValueKey").value:t.ɵɵpipeBind1(42,44,"tb.rulenode.output-value-key"),n.calculateDeltaConfigForm.get("periodValueKey").valid?n.calculateDeltaConfigForm.get("periodValueKey").value:t.ɵɵpipeBind1(43,46,"tb.rulenode.period-value-key"))):t.ɵɵpipeBind2(46,53,"tb.rulenode.exclude-zero-deltas-hint",t.ɵɵpureFunction1(67,Ei,n.calculateDeltaConfigForm.get("outputValueKey").valid?n.calculateDeltaConfigForm.get("outputValueKey").value:t.ɵɵpipeBind1(45,51,"tb.rulenode.output-value-key")))),t.ɵɵadvance(7),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(49,56,"tb.rulenode.exclude-zero-deltas")," "))},dependencies:t.ɵɵgetComponentDepsFactory(Mi),encapsulation:2})}}function Bi(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",8),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e.value),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.name," ")}}e("CalculateDeltaConfigComponent",Mi);class Vi extends i{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.fetchToData=[],this.DataToFetch=Ft;for(const e of qt.keys())e!==Ft.FIELDS&&this.fetchToData.push({value:e,name:this.translate.instant(qt.get(e))})}configForm(){return this.customerAttributesConfigForm}prepareOutputConfig(e){const t={};for(const n of Object.keys(e.dataMapping))t[n.trim()]=e.dataMapping[n];return e.dataMapping=t,_(e)}prepareInputConfig(e){let t,n;return t=P(e?.telemetry)?e.telemetry?Ft.LATEST_TELEMETRY:Ft.ATTRIBUTES:P(e?.dataToFetch)?e.dataToFetch:Ft.ATTRIBUTES,n=P(e?.attrMapping)?e.attrMapping:P(e?.dataMapping)?e.dataMapping:null,{dataToFetch:t,dataMapping:n,fetchTo:P(e?.fetchTo)?e.fetchTo:Kt.METADATA}}selectTranslation(e,t){return this.customerAttributesConfigForm.get("dataToFetch").value===Ft.LATEST_TELEMETRY?e:t}onConfigurationSet(e){this.customerAttributesConfigForm=this.fb.group({dataToFetch:[e.dataToFetch,[]],dataMapping:[e.dataMapping,[N.required]],fetchTo:[e.fetchTo]})}static{this.ɵfac=function(e){return new(e||Vi)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.FormBuilder),t.ɵɵdirectiveInject(K.TranslateService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Vi,selectors:[["tb-enrichment-node-customer-attributes-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:17,vars:26,consts:[[1,"tb-form-panel","stroked",3,"formGroup"],["translate","",1,"tb-form-panel-title"],[1,"flex","flex-1","items-center","justify-center"],[1,"fetch-to-data-toggle"],["formControlName","dataToFetch","appearance","fill",1,"fetch-to-data-toggle"],[3,"value",4,"ngFor","ngForOf"],["required","","formControlName","dataMapping","popupHelpLink","rulenode/customer_attributes_node_fields_templatization",3,"requiredText","labelText","keyText","keyRequiredText","valText","valRequiredText","hintText"],["formControlName","fetchTo",3,"labelText"],[3,"value"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1),t.ɵɵtext(2,"tb.rulenode.mapping-of-customers"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"div",2)(4,"div",3)(5,"tb-toggle-select",4),t.ɵɵtemplate(6,Bi,2,2,"tb-toggle-option",5),t.ɵɵelementEnd()()(),t.ɵɵelement(7,"tb-kv-map-config",6),t.ɵɵpipe(8,"translate"),t.ɵɵpipe(9,"translate"),t.ɵɵpipe(10,"translate"),t.ɵɵpipe(11,"translate"),t.ɵɵpipe(12,"translate"),t.ɵɵpipe(13,"translate"),t.ɵɵelement(14,"tb-msg-metadata-chip",7),t.ɵɵpipe(15,"translate"),t.ɵɵpipe(16,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.customerAttributesConfigForm),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",n.fetchToData),t.ɵɵadvance(),t.ɵɵproperty("requiredText",t.ɵɵpipeBind1(8,10,"tb.rulenode.attr-mapping-required"))("labelText",t.ɵɵpipeBind1(9,12,n.selectTranslation("tb.rulenode.latest-telemetry-mapping","tb.rulenode.attributes-mapping")))("keyText",t.ɵɵpipeBind1(10,14,n.selectTranslation("tb.rulenode.source-telemetry","tb.rulenode.source-attribute")))("keyRequiredText",t.ɵɵpipeBind1(11,16,n.selectTranslation("tb.rulenode.source-telemetry-required","tb.rulenode.source-attribute-required")))("valText",t.ɵɵpipeBind1(12,18,"tb.rulenode.target-key"))("valRequiredText",t.ɵɵpipeBind1(13,20,"tb.rulenode.target-key-required"))("hintText","tb.rulenode.kv-map-pattern-hint"),t.ɵɵadvance(7),t.ɵɵproperty("labelText",n.customerAttributesConfigForm.get("dataToFetch").value===n.DataToFetch.LATEST_TELEMETRY?t.ɵɵpipeBind1(15,22,"tb.rulenode.add-mapped-latest-telemetry-to"):t.ɵɵpipeBind1(16,24,"tb.rulenode.add-mapped-attribute-to")))},dependencies:t.ɵɵgetComponentDepsFactory(Vi),styles:["[_nghost-%COMP%] .fetch-to-data-toggle[_ngcontent-%COMP%]{max-width:420px;width:100%}"]})}}e("CustomerAttributesConfigComponent",Vi);class Oi extends i{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n}configForm(){return this.deviceAttributesConfigForm}onConfigurationSet(e){this.deviceAttributesConfigForm=this.fb.group({deviceRelationsQuery:[e.deviceRelationsQuery,[N.required]],tellFailureIfAbsent:[e.tellFailureIfAbsent,[]],fetchTo:[e.fetchTo,[]],attributesControl:[e.attributesControl,[]]})}prepareInputConfig(e){return j(e)&&(e.attributesControl={clientAttributeNames:P(e?.clientAttributeNames)?e.clientAttributeNames:[],latestTsKeyNames:P(e?.latestTsKeyNames)?e.latestTsKeyNames:[],serverAttributeNames:P(e?.serverAttributeNames)?e.serverAttributeNames:[],sharedAttributeNames:P(e?.sharedAttributeNames)?e.sharedAttributeNames:[],getLatestValueWithTs:!!P(e?.getLatestValueWithTs)&&e.getLatestValueWithTs}),{deviceRelationsQuery:P(e?.deviceRelationsQuery)?e.deviceRelationsQuery:null,tellFailureIfAbsent:!P(e?.tellFailureIfAbsent)||e.tellFailureIfAbsent,fetchTo:P(e?.fetchTo)?e.fetchTo:Kt.METADATA,attributesControl:e?e.attributesControl:null}}prepareOutputConfig(e){for(const t of Object.keys(e.attributesControl))e[t]=e.attributesControl[t];return delete e.attributesControl,e}static{this.ɵfac=function(e){return new(e||Oi)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(K.TranslateService),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Oi,selectors:[["tb-enrichment-node-device-attributes-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:19,vars:11,consts:[[1,"tb-form-panel","no-padding","no-border",3,"formGroup"],[1,"tb-form-panel","stroked","no-padding-bottom"],["translate","",1,"tb-form-panel-title"],["required","","formControlName","deviceRelationsQuery"],[1,"tb-form-panel","stroked"],[1,"tb-form-row","no-padding","no-border","space-between"],["translate","",1,"tb-form-panel-title","tb-required"],["translate","",1,"tb-form-panel-hint","tb-error",3,"hidden"],["formControlName","attributesControl","popupHelpLink","rulenode/related_device_attributes_node_fields_templatization"],["formControlName","fetchTo",3,"labelText"],[1,"tb-form-row","same-padding",3,"tb-hint-tooltip-icon"],["formControlName","tellFailureIfAbsent",1,"mat-slide","margin"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3,"tb.rulenode.device-relations-query"),t.ɵɵelementEnd(),t.ɵɵelement(4,"tb-device-relations-query-config",3),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",4)(6,"div",5)(7,"div",6),t.ɵɵtext(8,"tb.rulenode.related-device-attributes"),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"div",7),t.ɵɵtext(10," tb.rulenode.at-least-one-field-required "),t.ɵɵelementEnd()(),t.ɵɵelement(11,"tb-select-attributes",8)(12,"tb-msg-metadata-chip",9),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(14,"div",10),t.ɵɵpipe(15,"translate"),t.ɵɵelementStart(16,"mat-slide-toggle",11),t.ɵɵtext(17),t.ɵɵpipe(18,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.deviceAttributesConfigForm),t.ɵɵadvance(9),t.ɵɵproperty("hidden",!(n.deviceAttributesConfigForm.get("attributesControl").touched&&n.deviceAttributesConfigForm.get("attributesControl").hasError("atLeastOneRequired"))),t.ɵɵadvance(3),t.ɵɵproperty("labelText",t.ɵɵpipeBind1(13,5,"tb.rulenode.add-selected-attributes-to")),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(15,7,"tb.rulenode.tell-failure-tooltip")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(18,9,"tb.rulenode.tell-failure")," "))},dependencies:t.ɵɵgetComponentDepsFactory(Oi),encapsulation:2})}}e("DeviceAttributesConfigComponent",Oi);const Di=e=>({inputName:e});class Li extends i{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n,this.predefinedValues=[];for(const e of Object.keys(xt))this.predefinedValues.push({value:xt[e],name:this.translate.instant(Ct.get(xt[e]))})}ngOnInit(){super.ngOnInit()}configForm(){return this.entityDetailsConfigForm}prepareInputConfig(e){let t;return t=P(e?.addToMetadata)?e.addToMetadata?Kt.METADATA:Kt.DATA:e?.fetchTo?e.fetchTo:Kt.DATA,{detailsList:P(e?.detailsList)?e.detailsList:null,fetchTo:t}}onConfigurationSet(e){this.entityDetailsConfigForm=this.fb.group({detailsList:[e.detailsList,[N.required]],fetchTo:[e.fetchTo,[]]})}static{this.ɵfac=function(e){return new(e||Li)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(K.TranslateService),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Li,selectors:[["tb-enrichment-node-entity-details-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:11,vars:22,consts:[[3,"formGroup"],["required","","formControlName","detailsList",1,"mat-block",3,"predefinedValues","label","placeholder","requiredText"],["matSuffix","","aria-hidden","false","aria-label","help-icon","color","primary",1,"help-icon","margin-8","cursor-pointer",3,"matTooltip"],["formControlName","fetchTo",3,"labelText"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"tb-string-items-list",1),t.ɵɵpipe(2,"translate"),t.ɵɵpipe(3,"translate"),t.ɵɵpipe(4,"translate"),t.ɵɵelementStart(5,"mat-icon",2),t.ɵɵpipe(6,"translate"),t.ɵɵpipe(7,"translate"),t.ɵɵtext(8," help "),t.ɵɵelementEnd()(),t.ɵɵelement(9,"tb-msg-metadata-chip",3),t.ɵɵpipe(10,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.entityDetailsConfigForm),t.ɵɵadvance(),t.ɵɵproperty("predefinedValues",n.predefinedValues)("label",t.ɵɵpipeBind1(2,7,"tb.rulenode.select-details"))("placeholder",t.ɵɵpipeBind1(3,9,"tb.rulenode.add-detail"))("requiredText",t.ɵɵpipeBind1(4,11,"tb.rulenode.entity-details-list-empty")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind2(7,15,"tb.rulenode.chip-help",t.ɵɵpureFunction1(20,Di,t.ɵɵpipeBind1(6,13,"tb.rulenode.detail")))),t.ɵɵadvance(4),t.ɵɵproperty("labelText",t.ɵɵpipeBind1(10,18,"tb.rulenode.add-selected-details-to")))},dependencies:t.ɵɵgetComponentDepsFactory(Li),encapsulation:2})}}e("EntityDetailsConfigComponent",Li);const Pi=()=>({maxWidth:"820px"}),Ri=e=>({inputName:e}),_i=(e,t,n,r)=>({startInterval:e,endInterval:t,startIntervalTimeUnit:n,endIntervalTimeUnit:r});function ji(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.start-interval-value-required")," "))}function Gi(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.time-value-range")," "))}function Ki(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.time-value-range")," "))}function Ui(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",29),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.timeUnitsTranslationMap.get(e))," ")}}function Hi(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.end-interval-value-required")," "))}function zi(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.time-value-range")," "))}function $i(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.time-value-range")," "))}function Qi(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",29),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.timeUnitsTranslationMap.get(e))," ")}}function Ji(e,n){if(1&e&&(t.ɵɵelementContainerStart(0),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementContainerEnd()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind2(2,1,"tb.rulenode.fetch-timeseries-from-to",t.ɵɵpureFunction4(4,_i,e.getTelemetryFromDatabaseConfigForm.get("interval.startInterval").value,e.getTelemetryFromDatabaseConfigForm.get("interval.endInterval").value,e.getTelemetryFromDatabaseConfigForm.get("interval.startIntervalTimeUnit").value.toLowerCase(),e.getTelemetryFromDatabaseConfigForm.get("interval.endIntervalTimeUnit").value.toLowerCase()))," ")}}function Yi(e,n){1&e&&(t.ɵɵtext(0),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(1,1,"tb.rulenode.fetch-timeseries-from-to-invalid")," ")}function Wi(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",17)(1,"div",18)(2,"mat-form-field",19)(3,"mat-label"),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(6,"input",20),t.ɵɵtemplate(7,ji,3,3,"mat-error",16)(8,Gi,3,3,"mat-error",16)(9,Ki,3,3,"mat-error",16),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"mat-form-field",21)(11,"mat-label"),t.ɵɵtext(12),t.ɵɵpipe(13,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(14,"mat-select",22),t.ɵɵtemplate(15,Ui,3,4,"mat-option",14),t.ɵɵelementEnd()()(),t.ɵɵelementStart(16,"div",18)(17,"mat-form-field",19)(18,"mat-label"),t.ɵɵtext(19),t.ɵɵpipe(20,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(21,"input",23),t.ɵɵtemplate(22,Hi,3,3,"mat-error",16)(23,zi,3,3,"mat-error",16)(24,$i,3,3,"mat-error",16),t.ɵɵelementEnd(),t.ɵɵelementStart(25,"mat-form-field",21)(26,"mat-label"),t.ɵɵtext(27),t.ɵɵpipe(28,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(29,"mat-select",24),t.ɵɵtemplate(30,Qi,3,4,"mat-option",14),t.ɵɵelementEnd()()(),t.ɵɵelementStart(31,"div",25)(32,"mat-icon",26),t.ɵɵtext(33,"error_outline"),t.ɵɵelementEnd(),t.ɵɵelementStart(34,"div",27),t.ɵɵtemplate(35,Ji,3,9,"ng-container",28)(36,Yi,2,3,"ng-template",null,1,t.ɵɵtemplateRefExtractor),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵreference(37),n=t.ɵɵnextContext();t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(5,16,"tb.rulenode.interval-start")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.getTelemetryFromDatabaseConfigForm.get("interval.startInterval").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.getTelemetryFromDatabaseConfigForm.get("interval.startInterval").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.getTelemetryFromDatabaseConfigForm.get("interval.startInterval").hasError("max")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(13,18,"tb.rulenode.time-unit")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.timeUnits),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(20,20,"tb.rulenode.interval-end")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.getTelemetryFromDatabaseConfigForm.get("interval.endInterval").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.getTelemetryFromDatabaseConfigForm.get("interval.endInterval").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.getTelemetryFromDatabaseConfigForm.get("interval.endInterval").hasError("max")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(28,22,"tb.rulenode.time-unit")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.timeUnits),t.ɵɵadvance(),t.ɵɵclassProp("error",n.getTelemetryFromDatabaseConfigForm.get("interval").invalid),t.ɵɵadvance(4),t.ɵɵproperty("ngIf",n.getTelemetryFromDatabaseConfigForm.get("interval").valid)("ngIfElse",e)}}function Xi(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.start-interval-required")," "))}function Zi(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.end-interval-required")," "))}function eo(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",30)(1,"mat-form-field",31)(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"input",32),t.ɵɵtemplate(6,Xi,3,3,"mat-error",16),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-form-field",31)(8,"mat-label"),t.ɵɵtext(9),t.ɵɵpipe(10,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(11,"input",33),t.ɵɵtemplate(12,Zi,3,3,"mat-error",16),t.ɵɵelementEnd(),t.ɵɵelement(13,"tb-example-hint",34),t.ɵɵpipe(14,"translate"),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,5,"tb.rulenode.start-interval")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",e.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").hasError("required")||e.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").hasError("pattern")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(10,7,"tb.rulenode.end-interval")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",e.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").hasError("required")||e.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").hasError("pattern")),t.ɵɵadvance(),t.ɵɵproperty("hintText",t.ɵɵpipeBind1(14,9,"tb.rulenode.metadata-dynamic-interval-hint"))}}function to(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",29),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e.value),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.name," ")}}function no(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",29),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.aggregationTypesTranslations.get(r.aggregationTypes[e]))," ")}}function ro(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",29),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext(3);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.samplingOrdersTranslate.get(e))," ")}}function ao(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.limit-required")," "))}function io(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.limit-range")," "))}function oo(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.limit-range")," "))}function lo(e,n){if(1&e&&(t.ɵɵelementStart(0,"div")(1,"mat-form-field",37)(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-select",38),t.ɵɵtemplate(6,ro,3,4,"mat-option",14),t.ɵɵelementEnd()(),t.ɵɵelementStart(7,"mat-form-field",39)(8,"mat-label"),t.ɵɵtext(9),t.ɵɵpipe(10,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(11,"input",40),t.ɵɵelementStart(12,"mat-hint"),t.ɵɵtext(13),t.ɵɵpipe(14,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(15,ao,3,3,"mat-error",16)(16,io,3,3,"mat-error",16)(17,oo,3,3,"mat-error",16),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(4,7,"tb.rulenode.order-by-timestamp")," "),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",e.samplingOrders),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(10,9,"tb.rulenode.limit")),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(14,11,"tb.rulenode.limit-hint")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.getTelemetryFromDatabaseConfigForm.get("limit").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.getTelemetryFromDatabaseConfigForm.get("limit").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.getTelemetryFromDatabaseConfigForm.get("limit").hasError("max"))}}function so(e,n){if(1&e&&(t.ɵɵelementStart(0,"div")(1,"mat-form-field",35)(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-select",36),t.ɵɵtemplate(6,no,3,4,"mat-option",14),t.ɵɵelementEnd()(),t.ɵɵtemplate(7,lo,18,13,"div",16),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("subscriptSizing",e.defaultPaddingEnable()?"fixed":"dynamic"),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,4,"aggregation.function")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",e.aggregations),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.getTelemetryFromDatabaseConfigForm.get("aggregation").value===e.aggregationTypes.NONE)}}class po extends i{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n,this.separatorKeysCodes=[U,H,z],this.aggregationTypes=T,this.aggregations=Object.values(T),this.aggregationTypesTranslations=I,this.fetchMode=St,this.samplingOrders=Object.values(Et),this.samplingOrdersTranslate=kt,this.timeUnits=Object.values(ht),this.timeUnitsTranslationMap=yt,this.deduplicationStrategiesHintTranslations=It,this.headerOptions=[],this.timeUnitMap={[ht.MILLISECONDS]:1,[ht.SECONDS]:1e3,[ht.MINUTES]:6e4,[ht.HOURS]:36e5,[ht.DAYS]:864e5},this.intervalValidator=()=>e=>e.get("startInterval").value*this.timeUnitMap[e.get("startIntervalTimeUnit").value]<=e.get("endInterval").value*this.timeUnitMap[e.get("endIntervalTimeUnit").value]?{intervalError:!0}:null;for(const e of Tt.keys())this.headerOptions.push({value:e,name:this.translate.instant(Tt.get(e))})}configForm(){return this.getTelemetryFromDatabaseConfigForm}onConfigurationSet(e){this.getTelemetryFromDatabaseConfigForm=this.fb.group({latestTsKeyNames:[e.latestTsKeyNames,[N.required]],aggregation:[e.aggregation,[N.required]],fetchMode:[e.fetchMode,[N.required]],orderBy:[e.orderBy,[]],limit:[e.limit,[]],useMetadataIntervalPatterns:[e.useMetadataIntervalPatterns,[]],interval:this.fb.group({startInterval:[e.interval.startInterval,[]],startIntervalTimeUnit:[e.interval.startIntervalTimeUnit,[]],endInterval:[e.interval.endInterval,[]],endIntervalTimeUnit:[e.interval.endIntervalTimeUnit,[]]}),startIntervalPattern:[e.startIntervalPattern,[]],endIntervalPattern:[e.endIntervalPattern,[]]})}validatorTriggers(){return["fetchMode","useMetadataIntervalPatterns"]}toggleChange(e){this.getTelemetryFromDatabaseConfigForm.get("fetchMode").patchValue(e,{emitEvent:!0})}prepareOutputConfig(e){return e.startInterval=e.interval.startInterval,e.startIntervalTimeUnit=e.interval.startIntervalTimeUnit,e.endInterval=e.interval.endInterval,e.endIntervalTimeUnit=e.interval.endIntervalTimeUnit,delete e.interval,_(e)}prepareInputConfig(e){return j(e)&&(e.interval={startInterval:e.startInterval,startIntervalTimeUnit:e.startIntervalTimeUnit,endInterval:e.endInterval,endIntervalTimeUnit:e.endIntervalTimeUnit}),{latestTsKeyNames:P(e?.latestTsKeyNames)?e.latestTsKeyNames:null,aggregation:P(e?.aggregation)?e.aggregation:T.NONE,fetchMode:P(e?.fetchMode)?e.fetchMode:St.FIRST,orderBy:P(e?.orderBy)?e.orderBy:Et.ASC,limit:P(e?.limit)?e.limit:1e3,useMetadataIntervalPatterns:!!P(e?.useMetadataIntervalPatterns)&&e.useMetadataIntervalPatterns,interval:{startInterval:P(e?.interval?.startInterval)?e.interval.startInterval:2,startIntervalTimeUnit:P(e?.interval?.startIntervalTimeUnit)?e.interval.startIntervalTimeUnit:ht.MINUTES,endInterval:P(e?.interval?.endInterval)?e.interval.endInterval:1,endIntervalTimeUnit:P(e?.interval?.endIntervalTimeUnit)?e.interval.endIntervalTimeUnit:ht.MINUTES},startIntervalPattern:P(e?.startIntervalPattern)?e.startIntervalPattern:null,endIntervalPattern:P(e?.endIntervalPattern)?e.endIntervalPattern:null}}updateValidators(e){const t=this.getTelemetryFromDatabaseConfigForm.get("fetchMode").value,n=this.getTelemetryFromDatabaseConfigForm.get("useMetadataIntervalPatterns").value;t&&t===St.ALL?(this.getTelemetryFromDatabaseConfigForm.get("aggregation").setValidators([N.required]),this.getTelemetryFromDatabaseConfigForm.get("orderBy").setValidators([N.required]),this.getTelemetryFromDatabaseConfigForm.get("limit").setValidators([N.required,N.min(2),N.max(1e3)])):(this.getTelemetryFromDatabaseConfigForm.get("aggregation").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("orderBy").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("limit").setValidators([])),n?(this.getTelemetryFromDatabaseConfigForm.get("interval.startInterval").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("interval.startIntervalTimeUnit").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("interval.endInterval").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("interval.endIntervalTimeUnit").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("interval").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").setValidators([N.required,N.pattern(/(?:.|\s)*\S(&:.|\s)*/)]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").setValidators([N.required,N.pattern(/(?:.|\s)*\S(&:.|\s)*/)])):(this.getTelemetryFromDatabaseConfigForm.get("interval.startInterval").setValidators([N.required,N.min(1),N.max(2147483647)]),this.getTelemetryFromDatabaseConfigForm.get("interval.startIntervalTimeUnit").setValidators([N.required]),this.getTelemetryFromDatabaseConfigForm.get("interval.endInterval").setValidators([N.required,N.min(1),N.max(2147483647)]),this.getTelemetryFromDatabaseConfigForm.get("interval.endIntervalTimeUnit").setValidators([N.required]),this.getTelemetryFromDatabaseConfigForm.get("interval").setValidators([this.intervalValidator()]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").setValidators([])),this.getTelemetryFromDatabaseConfigForm.get("aggregation").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("orderBy").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("limit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval.startInterval").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval.startIntervalTimeUnit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval.endInterval").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval.endIntervalTimeUnit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").updateValueAndValidity({emitEvent:e})}removeKey(e,t){const n=this.getTelemetryFromDatabaseConfigForm.get(t).value,r=n.indexOf(e);r>=0&&(n.splice(r,1),this.getTelemetryFromDatabaseConfigForm.get(t).setValue(n,{emitEvent:!0}))}clearChipGrid(){this.getTelemetryFromDatabaseConfigForm.get("latestTsKeyNames").patchValue([],{emitEvent:!0})}addKey(e,t){const n=e.input;let r=e.value;if((r||"").trim()){r=r.trim();let e=this.getTelemetryFromDatabaseConfigForm.get(t).value;e&&-1!==e.indexOf(r)||(e||(e=[]),e.push(r),this.getTelemetryFromDatabaseConfigForm.get(t).setValue(e,{emitEvent:!0}))}n&&(n.value="")}defaultPaddingEnable(){return this.getTelemetryFromDatabaseConfigForm.get("fetchMode").value===St.ALL&&this.getTelemetryFromDatabaseConfigForm.get("aggregation").value===T.NONE}static{this.ɵfac=function(e){return new(e||po)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(K.TranslateService),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:po,selectors:[["tb-enrichment-node-get-telemetry-from-database"]],features:[t.ɵɵInheritDefinitionFeature],decls:34,vars:40,consts:[["intervalPattern",""],["invalidText",""],[1,"tb-form-panel","no-padding","no-border",3,"formGroup"],["editable","","subscriptSizing","dynamic","required","","formControlName","latestTsKeyNames",1,"mat-block",3,"placeholder","requiredText","label","hint"],["matHintEnd","","hintMode","","tb-help-popup-placement","right","trigger-style","letter-spacing:0.25px; font-size:12px",1,"see-example",3,"tb-help-popup","tb-help-popup-style","trigger-text"],["matSuffix","","aria-hidden","false","aria-label","help-icon","color","primary",1,"help-icon","margin-8","cursor-pointer",3,"matTooltip"],[1,"tb-form-panel","stroked"],["translate","",1,"tb-form-panel-title"],[1,"tb-form-row","no-border","no-padding",3,"tb-hint-tooltip-icon"],["formControlName","useMetadataIntervalPatterns",1,"mat-slide"],["formGroupName","interval","class","flex flex-col",4,"ngIf","ngIfElse"],[1,"tb-form-panel","no-border","no-padding","item-center"],[1,"fetch-mod-toggle"],["formControlName","fetchMode","appearance","fill"],[3,"value",4,"ngFor","ngForOf"],[1,"tb-form-hint","tb-primary-fill","hint-container"],[4,"ngIf"],["formGroupName","interval",1,"flex","flex-col"],[1,"flex","flex-col","gap-0","gt-sm:flex-row","gt-sm:gap-4"],[1,"mat-block","gt-sm:max-w-50%","gt-sm:flex-full"],["type","number","step","1","min","1","max","2147483647","matInput","","formControlName","startInterval","required",""],["hideRequiredMarker","",1,"mat-block","gt-sm:max-w-50%","gt-sm:flex-full"],["formControlName","startIntervalTimeUnit","required",""],["type","number","step","1","min","1","max","2147483647","matInput","","formControlName","endInterval","required",""],["formControlName","endIntervalTimeUnit","required",""],[1,"description-block","tb-primary-fill"],[1,"description-icon"],[1,"description-text"],[4,"ngIf","ngIfElse"],[3,"value"],[1,"input-block","flex","flex-col"],[1,"mat-block","flex-1"],["matInput","","formControlName","startIntervalPattern","required",""],["matInput","","formControlName","endIntervalPattern","required",""],["popupHelpLink","rulenode/originator_telemetry_node_fields_templatization",3,"hintText"],["hideRequiredMarker","",1,"mat-block",3,"subscriptSizing"],["formControlName","aggregation","required",""],["hideRequiredMarker","",1,"mat-block"],["formControlName","orderBy","required",""],[1,"mat-block"],["type","number","min","2","max","1000","step","1","matInput","","formControlName","limit","required",""]],template:function(e,n){if(1&e&&(t.ɵɵelementStart(0,"section",2)(1,"tb-string-items-list",3),t.ɵɵpipe(2,"translate"),t.ɵɵpipe(3,"translate"),t.ɵɵpipe(4,"translate"),t.ɵɵpipe(5,"translate"),t.ɵɵelement(6,"div",4),t.ɵɵpipe(7,"translate"),t.ɵɵelementStart(8,"mat-icon",5),t.ɵɵpipe(9,"translate"),t.ɵɵpipe(10,"translate"),t.ɵɵtext(11,"help "),t.ɵɵelementEnd()(),t.ɵɵelementStart(12,"div",6)(13,"div",7),t.ɵɵtext(14,"tb.rulenode.fetch-interval"),t.ɵɵelementEnd(),t.ɵɵelementStart(15,"div",8),t.ɵɵpipe(16,"translate"),t.ɵɵelementStart(17,"mat-slide-toggle",9),t.ɵɵtext(18),t.ɵɵpipe(19,"translate"),t.ɵɵelementEnd()(),t.ɵɵtemplate(20,Wi,38,24,"div",10)(21,eo,15,11,"ng-template",null,0,t.ɵɵtemplateRefExtractor),t.ɵɵelementEnd(),t.ɵɵelementStart(23,"div",6)(24,"div",7),t.ɵɵtext(25,"tb.rulenode.fetch-strategy"),t.ɵɵelementEnd(),t.ɵɵelementStart(26,"div",11)(27,"div",12)(28,"tb-toggle-select",13),t.ɵɵtemplate(29,to,2,2,"tb-toggle-option",14),t.ɵɵelementEnd()(),t.ɵɵelementStart(30,"div",15),t.ɵɵtext(31),t.ɵɵpipe(32,"translate"),t.ɵɵelementEnd()(),t.ɵɵtemplate(33,so,8,6,"div",16),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵreference(22);t.ɵɵproperty("formGroup",n.getTelemetryFromDatabaseConfigForm),t.ɵɵadvance(),t.ɵɵproperty("placeholder",t.ɵɵpipeBind1(2,16,"tb.rulenode.add-timeseries-key"))("requiredText",t.ɵɵpipeBind1(3,18,"tb.rulenode.timeseries-keys-required"))("label",t.ɵɵpipeBind1(4,20,"tb.rulenode.timeseries-keys"))("hint",t.ɵɵpipeBind1(5,22,"tb.rulenode.general-pattern-hint")),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("tb-help-popup","rulenode/originator_telemetry_node_fields_templatization"),t.ɵɵpropertyInterpolate("trigger-text",t.ɵɵpipeBind1(7,24,"tb.key-val.see-examples")),t.ɵɵproperty("tb-help-popup-style",t.ɵɵpureFunction0(37,Pi)),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind2(10,28,"tb.rulenode.chip-help",t.ɵɵpureFunction1(38,Ri,t.ɵɵpipeBind1(9,26,"tb.rulenode.timeseries-key")))),t.ɵɵadvance(7),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(16,31,"tb.rulenode.use-metadata-dynamic-interval-tooltip")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(19,33,"tb.rulenode.use-metadata-dynamic-interval")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",!1===n.getTelemetryFromDatabaseConfigForm.get("useMetadataIntervalPatterns").value)("ngIfElse",e),t.ɵɵadvance(9),t.ɵɵproperty("ngForOf",n.headerOptions),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(32,35,n.deduplicationStrategiesHintTranslations.get(n.getTelemetryFromDatabaseConfigForm.get("fetchMode").value))," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.getTelemetryFromDatabaseConfigForm.get("fetchMode").value===n.fetchMode.ALL)}},dependencies:t.ɵɵgetComponentDepsFactory(po),styles:["[_nghost-%COMP%] .see-example[_ngcontent-%COMP%]{display:inline-block}[_nghost-%COMP%] .description-block[_ngcontent-%COMP%]{display:flex;align-items:center;border-radius:6px;border:1px solid #EAEAEA}[_nghost-%COMP%] .description-block[_ngcontent-%COMP%] .description-icon[_ngcontent-%COMP%]{font-size:24px;height:24px;min-height:24px;width:24px;min-width:24px;line-height:24px;color:#d9d9d9;margin:4px}[_nghost-%COMP%] .description-block[_ngcontent-%COMP%] .description-text[_ngcontent-%COMP%]{font-size:12px;line-height:16px;letter-spacing:.25px;margin:6px}[_nghost-%COMP%] .description-block.error[_ngcontent-%COMP%]{color:var(--mdc-theme-error, #f44336)}[_nghost-%COMP%] .description-block.error[_ngcontent-%COMP%] .description-icon[_ngcontent-%COMP%]{color:var(--mdc-theme-error, #f44336)}[_nghost-%COMP%] .item-center[_ngcontent-%COMP%]{align-items:center}[_nghost-%COMP%] .item-center[_ngcontent-%COMP%] .fetch-mod-toggle[_ngcontent-%COMP%]{width:100%}[_nghost-%COMP%] .hint-container[_ngcontent-%COMP%]{width:100%}"]})}}e("GetTelemetryFromDatabaseConfigComponent",po);class mo extends i{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n}configForm(){return this.originatorAttributesConfigForm}onConfigurationSet(e){this.originatorAttributesConfigForm=this.fb.group({tellFailureIfAbsent:[e.tellFailureIfAbsent,[]],fetchTo:[e.fetchTo,[]],attributesControl:[e.attributesControl,[]]})}prepareInputConfig(e){return j(e)&&(e.attributesControl={clientAttributeNames:P(e?.clientAttributeNames)?e.clientAttributeNames:[],latestTsKeyNames:P(e?.latestTsKeyNames)?e.latestTsKeyNames:[],serverAttributeNames:P(e?.serverAttributeNames)?e.serverAttributeNames:[],sharedAttributeNames:P(e?.sharedAttributeNames)?e.sharedAttributeNames:[],getLatestValueWithTs:!!P(e?.getLatestValueWithTs)&&e.getLatestValueWithTs}),{fetchTo:P(e?.fetchTo)?e.fetchTo:Kt.METADATA,tellFailureIfAbsent:!!P(e?.tellFailureIfAbsent)&&e.tellFailureIfAbsent,attributesControl:P(e?.attributesControl)?e.attributesControl:null}}prepareOutputConfig(e){for(const t of Object.keys(e.attributesControl))e[t]=e.attributesControl[t];return delete e.attributesControl,e}static{this.ɵfac=function(e){return new(e||mo)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(K.TranslateService),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:mo,selectors:[["tb-enrichment-node-originator-attributes-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:15,vars:11,consts:[[1,"tb-form-panel","no-padding","no-border",3,"formGroup"],[1,"tb-form-panel","stroked"],[1,"tb-form-row","no-padding","no-border","space-between"],["translate","",1,"tb-form-panel-title","tb-required"],["translate","",1,"tb-form-panel-hint","tb-error",3,"hidden"],["formControlName","attributesControl","popupHelpLink","rulenode/originator_attributes_node_fields_templatization"],["formControlName","fetchTo",3,"labelText"],[1,"tb-form-row","same-padding",3,"tb-hint-tooltip-icon"],["formControlName","tellFailureIfAbsent",1,"mat-slide","margin"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1)(2,"div",2)(3,"div",3),t.ɵɵtext(4,"tb.rulenode.originator-attributes"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"div",4),t.ɵɵtext(6," tb.rulenode.at-least-one-field-required "),t.ɵɵelementEnd()(),t.ɵɵelement(7,"tb-select-attributes",5)(8,"tb-msg-metadata-chip",6),t.ɵɵpipe(9,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(10,"div",7),t.ɵɵpipe(11,"translate"),t.ɵɵelementStart(12,"mat-slide-toggle",8),t.ɵɵtext(13),t.ɵɵpipe(14,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.originatorAttributesConfigForm),t.ɵɵadvance(5),t.ɵɵproperty("hidden",!(n.originatorAttributesConfigForm.get("attributesControl").touched&&n.originatorAttributesConfigForm.get("attributesControl").hasError("atLeastOneRequired"))),t.ɵɵadvance(3),t.ɵɵproperty("labelText",t.ɵɵpipeBind1(9,5,"tb.rulenode.add-originator-attributes-to")),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(11,7,"tb.rulenode.tell-failure-tooltip")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(14,9,"tb.rulenode.tell-failure")," "))},dependencies:t.ɵɵgetComponentDepsFactory(mo),encapsulation:2})}}e("OriginatorAttributesConfigComponent",mo);class uo extends i{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.originatorFields=[];for(const e of ut)this.originatorFields.push({value:e.value,name:this.translate.instant(e.name)})}configForm(){return this.originatorFieldsConfigForm}prepareOutputConfig(e){return _(e)}prepareInputConfig(e){return{dataMapping:P(e?.dataMapping)?e.dataMapping:null,ignoreNullStrings:P(e?.ignoreNullStrings)?e.ignoreNullStrings:null,fetchTo:P(e?.fetchTo)?e.fetchTo:Kt.METADATA}}onConfigurationSet(e){this.originatorFieldsConfigForm=this.fb.group({dataMapping:[e.dataMapping,[N.required]],ignoreNullStrings:[e.ignoreNullStrings,[]],fetchTo:[e.fetchTo,[]]})}static{this.ɵfac=function(e){return new(e||uo)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.FormBuilder),t.ɵɵdirectiveInject(K.TranslateService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:uo,selectors:[["tb-enrichment-node-originator-fields-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:16,vars:32,consts:[[1,"tb-form-panel","no-padding","no-border",3,"formGroup"],["required","","targetKeyPrefix","originator","formControlName","dataMapping","popupHelpLink","rulenode/originator_fields_node_fields_templatization",3,"selectOptions","requiredText","labelText","selectText","selectRequiredText","valText","valRequiredText","hintText"],["formControlName","fetchTo",3,"labelText"],[1,"tb-form-row","same-padding",3,"tb-hint-tooltip-icon"],["formControlName","ignoreNullStrings",1,"mat-slide","margin"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0),t.ɵɵelement(1,"tb-sv-map-config",1),t.ɵɵpipe(2,"translate"),t.ɵɵpipe(3,"translate"),t.ɵɵpipe(4,"translate"),t.ɵɵpipe(5,"translate"),t.ɵɵpipe(6,"translate"),t.ɵɵpipe(7,"translate"),t.ɵɵpipe(8,"translate"),t.ɵɵelement(9,"tb-msg-metadata-chip",2),t.ɵɵpipe(10,"translate"),t.ɵɵelementStart(11,"div",3),t.ɵɵpipe(12,"translate"),t.ɵɵelementStart(13,"mat-slide-toggle",4),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.originatorFieldsConfigForm),t.ɵɵadvance(),t.ɵɵproperty("selectOptions",n.originatorFields)("requiredText",t.ɵɵpipeBind1(2,12,"tb.rulenode.attr-mapping-required"))("labelText",t.ɵɵpipeBind1(3,14,"tb.rulenode.originator-fields-mapping"))("selectText",t.ɵɵpipeBind1(4,16,"tb.rulenode.source-field"))("selectRequiredText",t.ɵɵpipeBind1(5,18,"tb.rulenode.source-field-required"))("valText",t.ɵɵpipeBind1(6,20,"tb.rulenode.target-key"))("valRequiredText",t.ɵɵpipeBind1(7,22,"tb.rulenode.target-key-required"))("hintText",t.ɵɵpipeBind1(8,24,"tb.rulenode.originator-fields-sv-map-hint")),t.ɵɵadvance(8),t.ɵɵproperty("labelText",t.ɵɵpipeBind1(10,26,"tb.rulenode.add-mapped-originator-fields-to")),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(12,28,"tb.rulenode.skip-empty-fields-tooltip")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(15,30,"tb.rulenode.skip-empty-fields")," "))},dependencies:t.ɵɵgetComponentDepsFactory(uo),encapsulation:2})}}function co(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",9),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e.value),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.name," ")}}e("OriginatorFieldsConfigComponent",uo);class fo extends i{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.DataToFetch=Ft,this.msgMetadataLabelTranslations=At,this.originatorFields=[],this.fetchToData=[];for(const e of Object.keys(ut))this.originatorFields.push({value:ut[e].value,name:this.translate.instant(ut[e].name)});for(const e of qt.keys())this.fetchToData.push({value:e,name:this.translate.instant(qt.get(e))})}configForm(){return this.relatedAttributesConfigForm}prepareOutputConfig(e){e.dataToFetch===Ft.FIELDS?(e.dataMapping=e.svMap,delete e.svMap):(e.dataMapping=e.kvMap,delete e.kvMap);const t={};if(e&&e.dataMapping)for(const n of Object.keys(e.dataMapping))t[n.trim()]=e.dataMapping[n];return e.dataMapping=t,delete e.svMap,delete e.kvMap,_(e)}prepareInputConfig(e){let t,n,r={[c.name.value]:`relatedEntity${this.translate.instant(c.name.name)}`},a={serialNumber:"sn"};return t=P(e?.telemetry)?e.telemetry?Ft.LATEST_TELEMETRY:Ft.ATTRIBUTES:P(e?.dataToFetch)?e.dataToFetch:Ft.ATTRIBUTES,n=P(e?.attrMapping)?e.attrMapping:P(e?.dataMapping)?e.dataMapping:null,t===Ft.FIELDS?r=n:a=n,{relationsQuery:P(e?.relationsQuery)?e.relationsQuery:null,dataToFetch:t,svMap:r,kvMap:a,fetchTo:P(e?.fetchTo)?e.fetchTo:Kt.METADATA}}selectTranslation(e,t){return this.relatedAttributesConfigForm.get("dataToFetch").value===Ft.LATEST_TELEMETRY?e:t}onConfigurationSet(e){this.relatedAttributesConfigForm=this.fb.group({relationsQuery:[e.relationsQuery,[N.required]],dataToFetch:[e.dataToFetch,[]],kvMap:[e.kvMap,[N.required]],svMap:[e.svMap,[N.required]],fetchTo:[e.fetchTo,[]]})}validatorTriggers(){return["dataToFetch"]}updateValidators(e){this.relatedAttributesConfigForm.get("dataToFetch").value===Ft.FIELDS?(this.relatedAttributesConfigForm.get("svMap").enable({emitEvent:!1}),this.relatedAttributesConfigForm.get("kvMap").disable({emitEvent:!1}),this.relatedAttributesConfigForm.get("svMap").updateValueAndValidity()):(this.relatedAttributesConfigForm.get("svMap").disable({emitEvent:!1}),this.relatedAttributesConfigForm.get("kvMap").enable({emitEvent:!1}),this.relatedAttributesConfigForm.get("kvMap").updateValueAndValidity())}static{this.ɵfac=function(e){return new(e||fo)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.FormBuilder),t.ɵɵdirectiveInject(K.TranslateService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:fo,selectors:[["tb-enrichment-node-related-attributes-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:24,vars:48,consts:[[1,"tb-form-panel","no-padding","no-border",3,"formGroup"],["required","","formControlName","relationsQuery"],[1,"tb-form-panel","stroked"],["translate","",1,"tb-form-panel-title"],["formControlName","dataToFetch","appearance","fill"],[3,"value",4,"ngFor","ngForOf"],["required","","formControlName","kvMap","popupHelpLink","rulenode/related_entity_data_node_fields_templatization",3,"hidden","requiredText","labelText","keyText","keyRequiredText","valText","valRequiredText","hintText"],["required","","targetKeyPrefix","relatedEntity","formControlName","svMap","popupHelpLink","rulenode/related_entity_data_node_fields_templatization",3,"hidden","labelText","selectOptions","requiredText","selectText","selectRequiredText","valText","valRequiredText","hintText"],["formControlName","fetchTo",3,"labelText"],[3,"value"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0),t.ɵɵelement(1,"tb-relations-query-config",1),t.ɵɵelementStart(2,"div",2)(3,"div",3),t.ɵɵtext(4,"tb.rulenode.data-to-fetch"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"tb-toggle-select",4),t.ɵɵtemplate(6,co,2,2,"tb-toggle-option",5),t.ɵɵelementEnd(),t.ɵɵelement(7,"tb-kv-map-config",6),t.ɵɵpipe(8,"translate"),t.ɵɵpipe(9,"translate"),t.ɵɵpipe(10,"translate"),t.ɵɵpipe(11,"translate"),t.ɵɵpipe(12,"translate"),t.ɵɵpipe(13,"translate"),t.ɵɵelement(14,"tb-sv-map-config",7),t.ɵɵpipe(15,"translate"),t.ɵɵpipe(16,"translate"),t.ɵɵpipe(17,"translate"),t.ɵɵpipe(18,"translate"),t.ɵɵpipe(19,"translate"),t.ɵɵpipe(20,"translate"),t.ɵɵpipe(21,"translate"),t.ɵɵelement(22,"tb-msg-metadata-chip",8),t.ɵɵpipe(23,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.relatedAttributesConfigForm),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",n.fetchToData),t.ɵɵadvance(),t.ɵɵproperty("hidden",n.relatedAttributesConfigForm.get("dataToFetch").value===n.DataToFetch.FIELDS)("requiredText",t.ɵɵpipeBind1(8,20,"tb.rulenode.attr-mapping-required"))("labelText",t.ɵɵpipeBind1(9,22,n.selectTranslation("tb.rulenode.latest-telemetry-mapping","tb.rulenode.attributes-mapping")))("keyText",t.ɵɵpipeBind1(10,24,n.selectTranslation("tb.rulenode.source-telemetry","tb.rulenode.source-attribute")))("keyRequiredText",t.ɵɵpipeBind1(11,26,n.selectTranslation("tb.rulenode.source-telemetry-required","tb.rulenode.source-attribute-required")))("valText",t.ɵɵpipeBind1(12,28,"tb.rulenode.target-key"))("valRequiredText",t.ɵɵpipeBind1(13,30,"tb.rulenode.target-key-required"))("hintText","tb.rulenode.kv-map-pattern-hint"),t.ɵɵadvance(7),t.ɵɵproperty("hidden",n.relatedAttributesConfigForm.get("dataToFetch").value!==n.DataToFetch.FIELDS)("labelText",t.ɵɵpipeBind1(15,32,"tb.rulenode.fields-mapping"))("selectOptions",n.originatorFields)("requiredText",t.ɵɵpipeBind1(16,34,"tb.rulenode.attr-mapping-required"))("selectText",t.ɵɵpipeBind1(17,36,"tb.rulenode.source-field"))("selectRequiredText",t.ɵɵpipeBind1(18,38,"tb.rulenode.source-field-required"))("valText",t.ɵɵpipeBind1(19,40,"tb.rulenode.target-key"))("valRequiredText",t.ɵɵpipeBind1(20,42,"tb.rulenode.target-key-required"))("hintText",t.ɵɵpipeBind1(21,44,"tb.rulenode.sv-map-hint")),t.ɵɵadvance(8),t.ɵɵproperty("labelText",t.ɵɵpipeBind1(23,46,n.msgMetadataLabelTranslations.get(n.relatedAttributesConfigForm.get("dataToFetch").value))))},dependencies:t.ɵɵgetComponentDepsFactory(fo),encapsulation:2})}}function go(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",8),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e.value),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.name," ")}}e("RelatedAttributesConfigComponent",fo);class ho extends i{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.fetchToData=[],this.DataToFetch=Ft;for(const e of qt.keys())e!==Ft.FIELDS&&this.fetchToData.push({value:e,name:this.translate.instant(qt.get(e))})}configForm(){return this.tenantAttributesConfigForm}prepareInputConfig(e){let t,n;return t=P(e?.telemetry)?e.telemetry?Ft.LATEST_TELEMETRY:Ft.ATTRIBUTES:P(e?.dataToFetch)?e.dataToFetch:Ft.ATTRIBUTES,n=P(e?.attrMapping)?e.attrMapping:P(e?.dataMapping)?e.dataMapping:null,{dataToFetch:t,dataMapping:n,fetchTo:P(e?.fetchTo)?e.fetchTo:Kt.METADATA}}selectTranslation(e,t){return this.tenantAttributesConfigForm.get("dataToFetch").value===Ft.LATEST_TELEMETRY?e:t}onConfigurationSet(e){this.tenantAttributesConfigForm=this.fb.group({dataToFetch:[e.dataToFetch,[]],dataMapping:[e.dataMapping,[N.required]],fetchTo:[e.fetchTo,[]]})}static{this.ɵfac=function(e){return new(e||ho)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.FormBuilder),t.ɵɵdirectiveInject(K.TranslateService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:ho,selectors:[["tb-enrichment-node-tenant-attributes-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:17,vars:26,consts:[[1,"tb-form-panel","stroked",3,"formGroup"],["translate","",1,"tb-form-panel-title"],[1,"flex","flex-1","items-center","justify-center"],[1,"fetch-to-data-toggle"],["formControlName","dataToFetch","appearance","fill",1,"fetch-to-data-toggle"],[3,"value",4,"ngFor","ngForOf"],["required","","formControlName","dataMapping","popupHelpLink","rulenode/tenant_attributes_node_fields_templatization",3,"requiredText","labelText","keyText","keyRequiredText","valText","valRequiredText","hintText"],["formControlName","fetchTo",3,"labelText"],[3,"value"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1),t.ɵɵtext(2,"tb.rulenode.mapping-of-tenant"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"div",2)(4,"div",3)(5,"tb-toggle-select",4),t.ɵɵtemplate(6,go,2,2,"tb-toggle-option",5),t.ɵɵelementEnd()()(),t.ɵɵelement(7,"tb-kv-map-config",6),t.ɵɵpipe(8,"translate"),t.ɵɵpipe(9,"translate"),t.ɵɵpipe(10,"translate"),t.ɵɵpipe(11,"translate"),t.ɵɵpipe(12,"translate"),t.ɵɵpipe(13,"translate"),t.ɵɵelement(14,"tb-msg-metadata-chip",7),t.ɵɵpipe(15,"translate"),t.ɵɵpipe(16,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.tenantAttributesConfigForm),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",n.fetchToData),t.ɵɵadvance(),t.ɵɵproperty("requiredText",t.ɵɵpipeBind1(8,10,"tb.rulenode.attr-mapping-required"))("labelText",t.ɵɵpipeBind1(9,12,n.selectTranslation("tb.rulenode.latest-telemetry-mapping","tb.rulenode.attributes-mapping")))("keyText",t.ɵɵpipeBind1(10,14,n.selectTranslation("tb.rulenode.source-telemetry","tb.rulenode.source-attribute")))("keyRequiredText",t.ɵɵpipeBind1(11,16,n.selectTranslation("tb.rulenode.source-telemetry-required","tb.rulenode.source-attribute-required")))("valText",t.ɵɵpipeBind1(12,18,"tb.rulenode.target-key"))("valRequiredText",t.ɵɵpipeBind1(13,20,"tb.rulenode.target-key-required"))("hintText","tb.rulenode.kv-map-pattern-hint"),t.ɵɵadvance(7),t.ɵɵproperty("labelText",n.tenantAttributesConfigForm.get("dataToFetch").value===n.DataToFetch.LATEST_TELEMETRY?t.ɵɵpipeBind1(15,22,"tb.rulenode.add-mapped-latest-telemetry-to"):t.ɵɵpipeBind1(16,24,"tb.rulenode.add-mapped-attribute-to")))},dependencies:t.ɵɵgetComponentDepsFactory(ho),styles:["[_nghost-%COMP%] .fetch-to-data-toggle[_ngcontent-%COMP%]{max-width:420px;width:100%}"]})}}e("TenantAttributesConfigComponent",ho);class yo extends i{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.fetchDeviceCredentialsConfigForm}prepareInputConfig(e){return{fetchTo:P(e?.fetchTo)?e.fetchTo:Kt.METADATA}}onConfigurationSet(e){this.fetchDeviceCredentialsConfigForm=this.fb.group({fetchTo:[e.fetchTo,[]]})}static{this.ɵfac=function(e){return new(e||yo)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:yo,selectors:[["tb-enrichment-node-fetch-device-credentials-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:3,vars:4,consts:[[3,"formGroup"],["formControlName","fetchTo",3,"labelText"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0),t.ɵɵelement(1,"tb-msg-metadata-chip",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.fetchDeviceCredentialsConfigForm),t.ɵɵadvance(),t.ɵɵproperty("labelText",t.ɵɵpipeBind1(2,2,"tb.rulenode.fetch-credentials-to")))},dependencies:t.ɵɵgetComponentDepsFactory(yo),encapsulation:2})}}e("FetchDeviceCredentialsConfigComponent",yo);class bo{static{this.ɵfac=function(e){return new(e||bo)}}static{this.ɵmod=t.ɵɵdefineNgModule({type:bo})}static{this.ɵinj=t.ɵɵdefineInjector({imports:[$,S,xi,Vi,Li,Oi,mo,uo,po,fo,ho,Mi,yo]})}}function vo(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.topic-required")," "))}function xo(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.hostname-required")," "))}function Co(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.device-id-required")," "))}function So(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",17),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.azureIotHubCredentialsTypeTranslationsMap.get(e))," ")}}function To(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.credentials-type-required")," "))}function Io(e,t){}function Eo(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.sas-key-required")," "))}function Fo(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"mat-form-field",5)(1,"mat-label",2),t.ɵɵtext(2,"tb.rulenode.sas-key"),t.ɵɵelementEnd(),t.ɵɵelement(3,"input",18)(4,"tb-toggle-password",19),t.ɵɵtemplate(5,Eo,3,3,"mat-error",4),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"tb-file-input",20),t.ɵɵpipe(7,"translate"),t.ɵɵpipe(8,"translate"),t.ɵɵlistener("fileNameChanged",(function(n){t.ɵɵrestoreView(e);const r=t.ɵɵnextContext();return t.ɵɵresetView(r.azureIotHubConfigForm.get("credentials.caCertFileName").setValue(n))})),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(5),t.ɵɵproperty("ngIf",e.azureIotHubConfigForm.get("credentials.sasKey").hasError("required")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(7,4,"tb.rulenode.azure-ca-cert")),t.ɵɵpropertyInterpolate("dropLabel",t.ɵɵpipeBind1(8,6,"tb.rulenode.drop-file")),t.ɵɵproperty("existingFileName",e.azureIotHubConfigForm.get("credentials.caCertFileName").value)}}function qo(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-file-input",20),t.ɵɵpipe(1,"translate"),t.ɵɵpipe(2,"translate"),t.ɵɵlistener("fileNameChanged",(function(n){t.ɵɵrestoreView(e);const r=t.ɵɵnextContext();return t.ɵɵresetView(r.azureIotHubConfigForm.get("credentials.caCertFileName").setValue(n))})),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"tb-file-input",21),t.ɵɵpipe(4,"translate"),t.ɵɵpipe(5,"translate"),t.ɵɵlistener("fileNameChanged",(function(n){t.ɵɵrestoreView(e);const r=t.ɵɵnextContext();return t.ɵɵresetView(r.azureIotHubConfigForm.get("credentials.certFileName").setValue(n))})),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"tb-file-input",22),t.ɵɵpipe(7,"translate"),t.ɵɵpipe(8,"translate"),t.ɵɵlistener("fileNameChanged",(function(n){t.ɵɵrestoreView(e);const r=t.ɵɵnextContext();return t.ɵɵresetView(r.azureIotHubConfigForm.get("credentials.privateKeyFileName").setValue(n))})),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"mat-form-field",5)(10,"mat-label",2),t.ɵɵtext(11,"tb.rulenode.private-key-password"),t.ɵɵelementEnd(),t.ɵɵelement(12,"input",23)(13,"tb-toggle-password",19),t.ɵɵelementEnd()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(1,9,"tb.rulenode.azure-ca-cert")),t.ɵɵpropertyInterpolate("dropLabel",t.ɵɵpipeBind1(2,11,"tb.rulenode.drop-file")),t.ɵɵproperty("existingFileName",e.azureIotHubConfigForm.get("credentials.caCertFileName").value),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(4,13,"tb.rulenode.cert")),t.ɵɵpropertyInterpolate("dropLabel",t.ɵɵpipeBind1(5,15,"tb.rulenode.drop-file")),t.ɵɵproperty("existingFileName",e.azureIotHubConfigForm.get("credentials.certFileName").value),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(7,17,"tb.rulenode.private-key")),t.ɵɵpropertyInterpolate("dropLabel",t.ɵɵpipeBind1(8,19,"tb.rulenode.drop-file")),t.ɵɵproperty("existingFileName",e.azureIotHubConfigForm.get("credentials.privateKeyFileName").value)}}e("RulenodeCoreConfigEnrichmentModule",bo),("undefined"==typeof ngJitMode||ngJitMode)&&t.ɵɵsetNgModuleScope(bo,{declarations:[Vi,Li,Oi,mo,uo,po,fo,ho,Mi,yo],imports:[$,S,xi],exports:[Vi,Li,Oi,mo,uo,po,fo,ho,Mi,yo]});class Ao extends i{constructor(e,t){super(e),this.store=e,this.fb=t,this.allAzureIotHubCredentialsTypes=Vt,this.azureIotHubCredentialsTypeTranslationsMap=Ot}configForm(){return this.azureIotHubConfigForm}onConfigurationSet(e){this.azureIotHubConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[N.required]],host:[e?e.host:null,[N.required]],port:[e?e.port:null,[N.required,N.min(1),N.max(65535)]],connectTimeoutSec:[e?e.connectTimeoutSec:null,[N.required,N.min(1),N.max(200)]],clientId:[e?e.clientId:null,[N.required]],cleanSession:[!!e&&e.cleanSession,[]],ssl:[!!e&&e.ssl,[]],credentials:this.fb.group({type:[e&&e.credentials?e.credentials.type:null,[N.required]],sasKey:[e&&e.credentials?e.credentials.sasKey:null,[]],caCert:[e&&e.credentials?e.credentials.caCert:null,[]],caCertFileName:[e&&e.credentials?e.credentials.caCertFileName:null,[]],privateKey:[e&&e.credentials?e.credentials.privateKey:null,[]],privateKeyFileName:[e&&e.credentials?e.credentials.privateKeyFileName:null,[]],cert:[e&&e.credentials?e.credentials.cert:null,[]],certFileName:[e&&e.credentials?e.credentials.certFileName:null,[]],password:[e&&e.credentials?e.credentials.password:null,[]]})})}prepareOutputConfig(e){const t=e.credentials.type;return"sas"===t&&(e.credentials={type:t,sasKey:e.credentials.sasKey,caCert:e.credentials.caCert,caCertFileName:e.credentials.caCertFileName}),e}validatorTriggers(){return["credentials.type"]}updateValidators(e){const t=this.azureIotHubConfigForm.get("credentials"),n=t.get("type").value;switch(e&&t.reset({type:n},{emitEvent:!1}),t.get("sasKey").setValidators([]),t.get("privateKey").setValidators([]),t.get("privateKeyFileName").setValidators([]),t.get("cert").setValidators([]),t.get("certFileName").setValidators([]),n){case"sas":t.get("sasKey").setValidators([N.required]);break;case"cert.PEM":t.get("privateKey").setValidators([N.required]),t.get("privateKeyFileName").setValidators([N.required]),t.get("cert").setValidators([N.required]),t.get("certFileName").setValidators([N.required])}t.get("sasKey").updateValueAndValidity({emitEvent:e}),t.get("privateKey").updateValueAndValidity({emitEvent:e}),t.get("privateKeyFileName").updateValueAndValidity({emitEvent:e}),t.get("cert").updateValueAndValidity({emitEvent:e}),t.get("certFileName").updateValueAndValidity({emitEvent:e})}static{this.ɵfac=function(e){return new(e||Ao)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Ao,selectors:[["tb-external-node-azure-iot-hub-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:37,vars:10,consts:[[1,"flex","flex-col",3,"formGroup"],["subscriptSizing","dynamic",1,"mat-block"],["translate",""],["required","","matInput","","formControlName","topicPattern"],[4,"ngIf"],[1,"mat-block"],["required","","matInput","","formControlName","host"],["required","","matInput","","formControlName","clientId","autocomplete","new-clientId"],[1,"tb-mqtt-credentials-panel-group"],["translate","",1,"tb-required"],["formGroupName","credentials",1,"flex","flex-col"],["formControlName","type","required",""],[3,"value",4,"ngFor","ngForOf"],[1,"flex","flex-col",3,"ngSwitch"],["ngSwitchCase","anonymous"],["ngSwitchCase","sas"],["ngSwitchCase","cert.PEM"],[3,"value"],["type","password","required","","matInput","","formControlName","sasKey","autocomplete","new-password"],["matSuffix",""],["formControlName","caCert","inputId","caCertSelect","noFileText","tb.rulenode.no-file",3,"fileNameChanged","existingFileName","label","dropLabel"],["formControlName","cert","inputId","CertSelect","required","","requiredAsError","","noFileText","tb.rulenode.no-file",3,"fileNameChanged","existingFileName","label","dropLabel"],["formControlName","privateKey","inputId","privateKeySelect","required","","requiredAsError","","noFileText","tb.rulenode.no-file",2,"padding-bottom","8px",3,"fileNameChanged","existingFileName","label","dropLabel"],["type","password","matInput","","formControlName","password","autocomplete","new-password"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"mat-form-field",1)(2,"mat-label",2),t.ɵɵtext(3,"tb.rulenode.topic"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",3),t.ɵɵtemplate(5,vo,3,3,"mat-error",4),t.ɵɵelementStart(6,"mat-hint",2),t.ɵɵtext(7,"tb.rulenode.general-pattern-hint"),t.ɵɵelementEnd()(),t.ɵɵelementStart(8,"mat-form-field",5)(9,"mat-label",2),t.ɵɵtext(10,"tb.rulenode.hostname"),t.ɵɵelementEnd(),t.ɵɵelement(11,"input",6),t.ɵɵtemplate(12,xo,3,3,"mat-error",4),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"mat-form-field",5)(14,"mat-label",2),t.ɵɵtext(15,"tb.rulenode.device-id"),t.ɵɵelementEnd(),t.ɵɵelement(16,"input",7),t.ɵɵtemplate(17,Co,3,3,"mat-error",4),t.ɵɵelementEnd(),t.ɵɵelementStart(18,"mat-accordion")(19,"mat-expansion-panel",8)(20,"mat-expansion-panel-header")(21,"mat-panel-title",9),t.ɵɵtext(22,"tb.rulenode.credentials"),t.ɵɵelementEnd(),t.ɵɵelementStart(23,"mat-panel-description"),t.ɵɵtext(24),t.ɵɵpipe(25,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(26,"section",10)(27,"mat-form-field",5)(28,"mat-label",2),t.ɵɵtext(29,"tb.rulenode.credentials-type"),t.ɵɵelementEnd(),t.ɵɵelementStart(30,"mat-select",11),t.ɵɵtemplate(31,So,3,4,"mat-option",12),t.ɵɵelementEnd(),t.ɵɵtemplate(32,To,3,3,"mat-error",4),t.ɵɵelementEnd(),t.ɵɵelementStart(33,"section",13),t.ɵɵtemplate(34,Io,0,0,"ng-template",14)(35,Fo,9,8,"ng-template",15)(36,qo,14,21,"ng-template",16),t.ɵɵelementEnd()()()()()),2&e&&(t.ɵɵproperty("formGroup",n.azureIotHubConfigForm),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.azureIotHubConfigForm.get("topicPattern").hasError("required")),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",n.azureIotHubConfigForm.get("host").hasError("required")),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.azureIotHubConfigForm.get("clientId").hasError("required")),t.ɵɵadvance(7),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(25,8,n.azureIotHubCredentialsTypeTranslationsMap.get(n.azureIotHubConfigForm.get("credentials.type").value))," "),t.ɵɵadvance(7),t.ɵɵproperty("ngForOf",n.allAzureIotHubCredentialsTypes),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.azureIotHubConfigForm.get("credentials.type").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngSwitch",n.azureIotHubConfigForm.get("credentials.type").value))},dependencies:t.ɵɵgetComponentDepsFactory(Ao),styles:["[_nghost-%COMP%] .tb-mqtt-credentials-panel-group[_ngcontent-%COMP%]{margin:0 6px}"]})}}function ko(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.topic-pattern-required")," "))}function No(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.bootstrap-servers-required")," "))}function wo(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.min-retries-message")," "))}function Mo(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.min-batch-size-bytes-message")," "))}function Bo(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.min-linger-ms-message")," "))}function Vo(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.min-buffer-memory-bytes-message")," "))}function Oo(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",21),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function Do(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.key-serializer-required")," "))}function Lo(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.value-serializer-required")," "))}function Po(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",21),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.ToByteStandartCharsetTypeTranslationMap.get(e))," ")}}function Ro(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",22)(1,"mat-label",2),t.ɵɵtext(2,"tb.rulenode.charset-encoding"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-select",23),t.ɵɵtemplate(4,Po,3,4,"mat-option",14),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(4),t.ɵɵproperty("ngForOf",e.ToByteStandartCharsetTypesValues)}}e("AzureIotHubConfigComponent",Ao);class _o extends i{constructor(e,t){super(e),this.store=e,this.fb=t,this.ackValues=["all","-1","0","1"],this.ToByteStandartCharsetTypesValues=Lt,this.ToByteStandartCharsetTypeTranslationMap=Pt}configForm(){return this.kafkaConfigForm}onConfigurationSet(e){this.kafkaConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[N.required]],keyPattern:[e?e.keyPattern:null],bootstrapServers:[e?e.bootstrapServers:null,[N.required]],retries:[e?e.retries:null,[N.min(0)]],batchSize:[e?e.batchSize:null,[N.min(0)]],linger:[e?e.linger:null,[N.min(0)]],bufferMemory:[e?e.bufferMemory:null,[N.min(0)]],acks:[e?e.acks:null,[N.required]],keySerializer:[e?e.keySerializer:null,[N.required]],valueSerializer:[e?e.valueSerializer:null,[N.required]],otherProperties:[e?e.otherProperties:null,[]],addMetadataKeyValuesAsKafkaHeaders:[!!e&&e.addMetadataKeyValuesAsKafkaHeaders,[]],kafkaHeadersCharset:[e?e.kafkaHeadersCharset:null,[]]})}validatorTriggers(){return["addMetadataKeyValuesAsKafkaHeaders"]}updateValidators(e){this.kafkaConfigForm.get("addMetadataKeyValuesAsKafkaHeaders").value?this.kafkaConfigForm.get("kafkaHeadersCharset").setValidators([N.required]):this.kafkaConfigForm.get("kafkaHeadersCharset").setValidators([]),this.kafkaConfigForm.get("kafkaHeadersCharset").updateValueAndValidity({emitEvent:e})}static{this.ɵfac=function(e){return new(e||_o)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:_o,selectors:[["tb-external-node-kafka-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:65,vars:14,consts:[[1,"flex","flex-col",3,"formGroup"],["subscriptSizing","dynamic",1,"mat-block"],["translate",""],["required","","matInput","","formControlName","topicPattern"],[4,"ngIf"],["matInput","","formControlName","keyPattern"],["translate","",1,"tb-hint"],[1,"mat-block"],["required","","matInput","","formControlName","bootstrapServers"],["type","number","step","1","min","0","matInput","","formControlName","retries"],["type","number","step","1","min","0","matInput","","formControlName","batchSize"],["type","number","step","1","min","0","matInput","","formControlName","linger"],["type","number","step","1","min","0","matInput","","formControlName","bufferMemory"],["formControlName","acks","required",""],[3,"value",4,"ngFor","ngForOf"],["required","","matInput","","formControlName","keySerializer"],["required","","matInput","","formControlName","valueSerializer"],["translate","",1,"tb-title"],["required","false","formControlName","otherProperties","keyText","tb.rulenode.key","keyRequiredText","tb.rulenode.key-required","valText","tb.rulenode.value","valRequiredText","tb.rulenode.value-required"],["formControlName","addMetadataKeyValuesAsKafkaHeaders",1,"flex-1"],["class","mat-block flex-1",4,"ngIf"],[3,"value"],[1,"mat-block","flex-1"],["formControlName","kafkaHeadersCharset","required",""]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"mat-form-field",1)(2,"mat-label",2),t.ɵɵtext(3,"tb.rulenode.topic-pattern"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",3),t.ɵɵtemplate(5,ko,3,3,"mat-error",4),t.ɵɵelementStart(6,"mat-hint",2),t.ɵɵtext(7,"tb.rulenode.general-pattern-hint"),t.ɵɵelementEnd()(),t.ɵɵelementStart(8,"mat-form-field",1)(9,"mat-label",2),t.ɵɵtext(10,"tb.rulenode.key-pattern"),t.ɵɵelementEnd(),t.ɵɵelement(11,"input",5),t.ɵɵelementStart(12,"mat-hint",2),t.ɵɵtext(13,"tb.rulenode.general-pattern-hint"),t.ɵɵelementEnd()(),t.ɵɵelementStart(14,"div",6),t.ɵɵtext(15,"tb.rulenode.key-pattern-hint"),t.ɵɵelementEnd(),t.ɵɵelementStart(16,"mat-form-field",7)(17,"mat-label",2),t.ɵɵtext(18,"tb.rulenode.bootstrap-servers"),t.ɵɵelementEnd(),t.ɵɵelement(19,"input",8),t.ɵɵtemplate(20,No,3,3,"mat-error",4),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"mat-form-field",7)(22,"mat-label",2),t.ɵɵtext(23,"tb.rulenode.retries"),t.ɵɵelementEnd(),t.ɵɵelement(24,"input",9),t.ɵɵtemplate(25,wo,3,3,"mat-error",4),t.ɵɵelementEnd(),t.ɵɵelementStart(26,"mat-form-field",7)(27,"mat-label",2),t.ɵɵtext(28,"tb.rulenode.batch-size-bytes"),t.ɵɵelementEnd(),t.ɵɵelement(29,"input",10),t.ɵɵtemplate(30,Mo,3,3,"mat-error",4),t.ɵɵelementEnd(),t.ɵɵelementStart(31,"mat-form-field",7)(32,"mat-label",2),t.ɵɵtext(33,"tb.rulenode.linger-ms"),t.ɵɵelementEnd(),t.ɵɵelement(34,"input",11),t.ɵɵtemplate(35,Bo,3,3,"mat-error",4),t.ɵɵelementEnd(),t.ɵɵelementStart(36,"mat-form-field",7)(37,"mat-label",2),t.ɵɵtext(38,"tb.rulenode.buffer-memory-bytes"),t.ɵɵelementEnd(),t.ɵɵelement(39,"input",12),t.ɵɵtemplate(40,Vo,3,3,"mat-error",4),t.ɵɵelementEnd(),t.ɵɵelementStart(41,"mat-form-field",7)(42,"mat-label",2),t.ɵɵtext(43,"tb.rulenode.acks"),t.ɵɵelementEnd(),t.ɵɵelementStart(44,"mat-select",13),t.ɵɵtemplate(45,Oo,2,2,"mat-option",14),t.ɵɵelementEnd()(),t.ɵɵelementStart(46,"mat-form-field",7)(47,"mat-label",2),t.ɵɵtext(48,"tb.rulenode.key-serializer"),t.ɵɵelementEnd(),t.ɵɵelement(49,"input",15),t.ɵɵtemplate(50,Do,3,3,"mat-error",4),t.ɵɵelementEnd(),t.ɵɵelementStart(51,"mat-form-field",7)(52,"mat-label",2),t.ɵɵtext(53,"tb.rulenode.value-serializer"),t.ɵɵelementEnd(),t.ɵɵelement(54,"input",16),t.ɵɵtemplate(55,Lo,3,3,"mat-error",4),t.ɵɵelementEnd(),t.ɵɵelementStart(56,"label",17),t.ɵɵtext(57,"tb.rulenode.other-properties"),t.ɵɵelementEnd(),t.ɵɵelement(58,"tb-kv-map-config-old",18),t.ɵɵelementStart(59,"mat-checkbox",19),t.ɵɵtext(60),t.ɵɵpipe(61,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(62,"div",6),t.ɵɵtext(63,"tb.rulenode.add-metadata-key-values-as-kafka-headers-hint"),t.ɵɵelementEnd(),t.ɵɵtemplate(64,Ro,5,1,"mat-form-field",20),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.kafkaConfigForm),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.kafkaConfigForm.get("topicPattern").hasError("required")),t.ɵɵadvance(15),t.ɵɵproperty("ngIf",n.kafkaConfigForm.get("bootstrapServers").hasError("required")),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.kafkaConfigForm.get("retries").hasError("min")),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.kafkaConfigForm.get("batchSize").hasError("min")),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.kafkaConfigForm.get("linger").hasError("min")),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.kafkaConfigForm.get("bufferMemory").hasError("min")),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.ackValues),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.kafkaConfigForm.get("keySerializer").hasError("required")),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.kafkaConfigForm.get("valueSerializer").hasError("required")),t.ɵɵadvance(5),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(61,12,"tb.rulenode.add-metadata-key-values-as-kafka-headers")," "),t.ɵɵadvance(4),t.ɵɵproperty("ngIf",n.kafkaConfigForm.get("addMetadataKeyValuesAsKafkaHeaders").value))},dependencies:t.ɵɵgetComponentDepsFactory(_o),encapsulation:2})}}function jo(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.topic-pattern-required")," "))}function Go(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.host-required")," "))}function Ko(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.port-required")," "))}function Uo(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.port-range")," "))}function Ho(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.port-range")," "))}function zo(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.connect-timeout-required")," "))}function $o(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.connect-timeout-range")," "))}function Qo(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.connect-timeout-range")," "))}e("KafkaConfigComponent",_o);class Jo extends i{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.mqttConfigForm}onConfigurationSet(e){this.mqttConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[N.required]],host:[e?e.host:null,[N.required]],port:[e?e.port:null,[N.required,N.min(1),N.max(65535)]],connectTimeoutSec:[e?e.connectTimeoutSec:null,[N.required,N.min(1),N.max(200)]],clientId:[e?e.clientId:null,[]],appendClientIdSuffix:[{value:!!e&&e.appendClientIdSuffix,disabled:!(e&&G(e.clientId))},[]],parseToPlainText:[!!e&&e.parseToPlainText,[]],cleanSession:[!!e&&e.cleanSession,[]],retainedMessage:[!!e&&e.retainedMessage,[]],ssl:[!!e&&e.ssl,[]],credentials:[e?e.credentials:null,[]]})}updateValidators(e){G(this.mqttConfigForm.get("clientId").value)?this.mqttConfigForm.get("appendClientIdSuffix").enable({emitEvent:!1}):this.mqttConfigForm.get("appendClientIdSuffix").disable({emitEvent:!1}),this.mqttConfigForm.get("appendClientIdSuffix").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["clientId"]}static{this.ɵfac=function(e){return new(e||Jo)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Jo,selectors:[["tb-external-node-mqtt-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:57,vars:34,consts:[[1,"flex","flex-col",3,"formGroup"],["subscriptSizing","dynamic",1,"mat-block"],["translate",""],["required","","matInput","","formControlName","topicPattern"],[4,"ngIf"],[1,"flex","flex-1","flex-col","gt-sm:flex-row","gt-sm:gap-2"],[1,"mat-block","gt-sm:max-w-60%","gt-sm:flex-full"],["required","","matInput","","formControlName","host"],[1,"mat-block","gt-sm:max-w-40%","gt-sm:flex-full"],["required","","type","number","step","1","min","1","max","65535","matInput","","formControlName","port"],["required","","type","number","step","1","min","1","max","200","matInput","","formControlName","connectTimeoutSec"],["matInput","","formControlName","clientId"],["formControlName","appendClientIdSuffix"],[1,"tb-hint"],["formControlName","parseToPlainText"],["formControlName","cleanSession"],["formControlName","retainedMessage"],["formControlName","ssl"],["formControlName","credentials",3,"passwordFieldRequired"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"mat-form-field",1)(2,"mat-label",2),t.ɵɵtext(3,"tb.rulenode.topic-pattern"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",3),t.ɵɵtemplate(5,jo,3,3,"mat-error",4),t.ɵɵelementStart(6,"mat-hint",2),t.ɵɵtext(7,"tb.rulenode.general-pattern-hint"),t.ɵɵelementEnd()(),t.ɵɵelementStart(8,"div",5)(9,"mat-form-field",6)(10,"mat-label",2),t.ɵɵtext(11,"tb.rulenode.host"),t.ɵɵelementEnd(),t.ɵɵelement(12,"input",7),t.ɵɵtemplate(13,Go,3,3,"mat-error",4),t.ɵɵelementEnd(),t.ɵɵelementStart(14,"mat-form-field",8)(15,"mat-label",2),t.ɵɵtext(16,"tb.rulenode.port"),t.ɵɵelementEnd(),t.ɵɵelement(17,"input",9),t.ɵɵtemplate(18,Ko,3,3,"mat-error",4)(19,Uo,3,3,"mat-error",4)(20,Ho,3,3,"mat-error",4),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"mat-form-field",8)(22,"mat-label",2),t.ɵɵtext(23,"tb.rulenode.connect-timeout"),t.ɵɵelementEnd(),t.ɵɵelement(24,"input",10),t.ɵɵtemplate(25,zo,3,3,"mat-error",4)(26,$o,3,3,"mat-error",4)(27,Qo,3,3,"mat-error",4),t.ɵɵelementEnd()(),t.ɵɵelementStart(28,"mat-form-field",1)(29,"mat-label",2),t.ɵɵtext(30,"tb.rulenode.client-id"),t.ɵɵelementEnd(),t.ɵɵelement(31,"input",11),t.ɵɵelementStart(32,"mat-hint"),t.ɵɵtext(33),t.ɵɵpipe(34,"translate"),t.ɵɵelementEnd()(),t.ɵɵelementStart(35,"mat-checkbox",12),t.ɵɵtext(36),t.ɵɵpipe(37,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(38,"div",13),t.ɵɵtext(39),t.ɵɵpipe(40,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(41,"mat-checkbox",14),t.ɵɵtext(42),t.ɵɵpipe(43,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(44,"div",13),t.ɵɵtext(45),t.ɵɵpipe(46,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(47,"mat-checkbox",15),t.ɵɵtext(48),t.ɵɵpipe(49,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(50,"mat-checkbox",16),t.ɵɵtext(51),t.ɵɵpipe(52,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(53,"mat-checkbox",17),t.ɵɵtext(54),t.ɵɵpipe(55,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(56,"tb-credentials-config",18),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.mqttConfigForm),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.mqttConfigForm.get("topicPattern").hasError("required")),t.ɵɵadvance(8),t.ɵɵproperty("ngIf",n.mqttConfigForm.get("host").hasError("required")),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.mqttConfigForm.get("port").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.mqttConfigForm.get("port").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.mqttConfigForm.get("port").hasError("max")),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.mqttConfigForm.get("connectTimeoutSec").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.mqttConfigForm.get("connectTimeoutSec").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.mqttConfigForm.get("connectTimeoutSec").hasError("max")),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(34,18,"tb.rulenode.client-id-hint")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(37,20,"tb.rulenode.append-client-id-suffix")," "),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(40,22,"tb.rulenode.client-id-suffix-hint")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(43,24,"tb.rulenode.parse-to-plain-text")," "),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(46,26,"tb.rulenode.parse-to-plain-text-hint")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(49,28,"tb.rulenode.clean-session")," "),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(52,30,"tb.rulenode.retained-message")," "),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(55,32,"tb.rulenode.enable-ssl")," "),t.ɵɵadvance(2),t.ɵɵproperty("passwordFieldRequired",!1))},dependencies:t.ɵɵgetComponentDepsFactory(Jo),styles:["[_nghost-%COMP%] .tb-mqtt-credentials-panel-group[_ngcontent-%COMP%]{margin:0 6px}"]})}}e("MqttConfigComponent",Jo);class Yo extends i{constructor(e,t){super(e),this.store=e,this.fb=t,this.notificationType=E,this.entityType=u}configForm(){return this.notificationConfigForm}onConfigurationSet(e){this.notificationConfigForm=this.fb.group({templateId:[e?e.templateId:null,[N.required]],targets:[e?e.targets:[],[N.required]]})}static{this.ɵfac=function(e){return new(e||Yo)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Yo,selectors:[["tb-external-node-notification-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:6,vars:13,consts:[[1,"flex","flex-col",3,"formGroup"],["required","","allowCreate","","formControlName","templateId",3,"notificationTypes"],["required","","formControlName","targets",3,"labelText","placeholderText","requiredText","entityType","subType"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0),t.ɵɵelement(1,"tb-template-autocomplete",1)(2,"tb-entity-list",2),t.ɵɵpipe(3,"translate"),t.ɵɵpipe(4,"translate"),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.notificationConfigForm),t.ɵɵadvance(),t.ɵɵproperty("notificationTypes",n.notificationType.RULE_NODE),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("labelText",t.ɵɵpipeBind1(3,7,"notification.recipients")),t.ɵɵpropertyInterpolate("placeholderText",t.ɵɵpipeBind1(4,9,"notification.recipient")),t.ɵɵpropertyInterpolate("requiredText",t.ɵɵpipeBind1(5,11,"notification.recipients-required")),t.ɵɵpropertyInterpolate("entityType",n.entityType.NOTIFICATION_TARGET),t.ɵɵpropertyInterpolate("subType",n.notificationType.RULE_NODE))},dependencies:t.ɵɵgetComponentDepsFactory(Yo),encapsulation:2})}}function Wo(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.gcp-project-id-required")," "))}function Xo(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.pubsub-topic-name-required")," "))}e("NotificationConfigComponent",Yo);class Zo extends i{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.pubSubConfigForm}onConfigurationSet(e){this.pubSubConfigForm=this.fb.group({projectId:[e?e.projectId:null,[N.required]],topicName:[e?e.topicName:null,[N.required]],serviceAccountKey:[e?e.serviceAccountKey:null,[N.required]],serviceAccountKeyFileName:[e?e.serviceAccountKeyFileName:null,[N.required]],messageAttributes:[e?e.messageAttributes:null,[]]})}static{this.ɵfac=function(e){return new(e||Zo)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Zo,selectors:[["tb-external-node-pub-sub-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:20,vars:16,consts:[[1,"flex","flex-col",3,"formGroup"],[1,"mat-block"],["translate",""],["required","","matInput","","formControlName","projectId"],[4,"ngIf"],["required","","matInput","","formControlName","topicName"],["formControlName","serviceAccountKey","required","","requiredAsError","","noFileText","tb.rulenode.no-file",2,"padding-bottom","24px",3,"fileNameChanged","existingFileName","label","dropLabel"],["translate","",1,"tb-title"],[1,"tb-hint",3,"innerHTML"],["required","false","formControlName","messageAttributes","keyText","tb.rulenode.name","keyRequiredText","tb.rulenode.name-required","valText","tb.rulenode.value","valRequiredText","tb.rulenode.value-required"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"mat-form-field",1)(2,"mat-label",2),t.ɵɵtext(3,"tb.rulenode.gcp-project-id"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",3),t.ɵɵtemplate(5,Wo,3,3,"mat-error",4),t.ɵɵelementEnd(),t.ɵɵelementStart(6,"mat-form-field",1)(7,"mat-label",2),t.ɵɵtext(8,"tb.rulenode.pubsub-topic-name"),t.ɵɵelementEnd(),t.ɵɵelement(9,"input",5),t.ɵɵtemplate(10,Xo,3,3,"mat-error",4),t.ɵɵelementEnd(),t.ɵɵelementStart(11,"tb-file-input",6),t.ɵɵpipe(12,"translate"),t.ɵɵpipe(13,"translate"),t.ɵɵlistener("fileNameChanged",(function(e){return n.pubSubConfigForm.get("serviceAccountKeyFileName").setValue(e)})),t.ɵɵelementEnd(),t.ɵɵelementStart(14,"label",7),t.ɵɵtext(15,"tb.rulenode.message-attributes"),t.ɵɵelementEnd(),t.ɵɵelement(16,"div",8),t.ɵɵpipe(17,"translate"),t.ɵɵpipe(18,"safe"),t.ɵɵelement(19,"tb-kv-map-config-old",9),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.pubSubConfigForm),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.pubSubConfigForm.get("projectId").hasError("required")),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.pubSubConfigForm.get("topicName").hasError("required")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("label",t.ɵɵpipeBind1(12,7,"tb.rulenode.gcp-service-account-key")),t.ɵɵpropertyInterpolate("dropLabel",t.ɵɵpipeBind1(13,9,"tb.rulenode.drop-file")),t.ɵɵproperty("existingFileName",n.pubSubConfigForm.get("serviceAccountKeyFileName").value),t.ɵɵadvance(5),t.ɵɵproperty("innerHTML",t.ɵɵpipeBind2(18,13,t.ɵɵpipeBind1(17,11,"tb.rulenode.message-attributes-hint"),"html"),t.ɵɵsanitizeHtml))},dependencies:t.ɵɵgetComponentDepsFactory(Zo),encapsulation:2})}}function el(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",22),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function tl(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.host-required")," "))}function nl(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.port-required")," "))}function rl(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.port-range")," "))}function al(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.port-range")," "))}function il(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.min-connection-timeout-ms-message")," "))}function ol(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.min-handshake-timeout-ms-message")," "))}e("PubSubConfigComponent",Zo);class ll extends i{constructor(e,t){super(e),this.store=e,this.fb=t,this.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"]}configForm(){return this.rabbitMqConfigForm}onConfigurationSet(e){this.rabbitMqConfigForm=this.fb.group({exchangeNamePattern:[e?e.exchangeNamePattern:null,[]],routingKeyPattern:[e?e.routingKeyPattern:null,[]],messageProperties:[e?e.messageProperties:null,[]],host:[e?e.host:null,[N.required]],port:[e?e.port:null,[N.required,N.min(1),N.max(65535)]],virtualHost:[e?e.virtualHost:null,[]],username:[e?e.username:null,[]],password:[e?e.password:null,[]],automaticRecoveryEnabled:[!!e&&e.automaticRecoveryEnabled,[]],connectionTimeout:[e?e.connectionTimeout:null,[N.min(0)]],handshakeTimeout:[e?e.handshakeTimeout:null,[N.min(0)]],clientProperties:[e?e.clientProperties:null,[]]})}static{this.ɵfac=function(e){return new(e||ll)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:ll,selectors:[["tb-external-node-rabbit-mq-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:56,vars:11,consts:[[1,"flex","flex-col",3,"formGroup"],[1,"mat-block"],["translate",""],["matInput","","formControlName","exchangeNamePattern"],["matInput","","formControlName","routingKeyPattern"],["formControlName","messageProperties"],[3,"value",4,"ngFor","ngForOf"],[1,"gt-sm:flex","gt-sm:flex-row","gt-sm:gap-2"],[1,"mat-block","gt-sm:max-w-60%","gt-sm:flex-full"],["required","","matInput","","formControlName","host"],[4,"ngIf"],[1,"mat-block","gt-sm:max-w-40%","gt-sm:flex-full"],["required","","type","number","step","1","min","1","max","65535","matInput","","formControlName","port"],["matInput","","formControlName","virtualHost"],["matInput","","formControlName","username"],["type","password","matInput","","formControlName","password"],["matSuffix",""],["formControlName","automaticRecoveryEnabled"],["type","number","step","1","min","0","matInput","","formControlName","connectionTimeout"],["type","number","step","1","min","0","matInput","","formControlName","handshakeTimeout"],["translate","",1,"tb-title"],["required","false","formControlName","clientProperties","keyText","tb.rulenode.key","keyRequiredText","tb.rulenode.key-required","valText","tb.rulenode.value","valRequiredText","tb.rulenode.value-required"],[3,"value"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"mat-form-field",1)(2,"mat-label",2),t.ɵɵtext(3,"tb.rulenode.exchange-name-pattern"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",3),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-form-field",1)(6,"mat-label",2),t.ɵɵtext(7,"tb.rulenode.routing-key-pattern"),t.ɵɵelementEnd(),t.ɵɵelement(8,"input",4),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"mat-form-field",1)(10,"mat-label",2),t.ɵɵtext(11,"tb.rulenode.message-properties"),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"mat-select",5),t.ɵɵtemplate(13,el,2,2,"mat-option",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(14,"div",7)(15,"mat-form-field",8)(16,"mat-label",2),t.ɵɵtext(17,"tb.rulenode.host"),t.ɵɵelementEnd(),t.ɵɵelement(18,"input",9),t.ɵɵtemplate(19,tl,3,3,"mat-error",10),t.ɵɵelementEnd(),t.ɵɵelementStart(20,"mat-form-field",11)(21,"mat-label",2),t.ɵɵtext(22,"tb.rulenode.port"),t.ɵɵelementEnd(),t.ɵɵelement(23,"input",12),t.ɵɵtemplate(24,nl,3,3,"mat-error",10)(25,rl,3,3,"mat-error",10)(26,al,3,3,"mat-error",10),t.ɵɵelementEnd()(),t.ɵɵelementStart(27,"mat-form-field",1)(28,"mat-label",2),t.ɵɵtext(29,"tb.rulenode.virtual-host"),t.ɵɵelementEnd(),t.ɵɵelement(30,"input",13),t.ɵɵelementEnd(),t.ɵɵelementStart(31,"mat-form-field",1)(32,"mat-label",2),t.ɵɵtext(33,"tb.rulenode.username"),t.ɵɵelementEnd(),t.ɵɵelement(34,"input",14),t.ɵɵelementEnd(),t.ɵɵelementStart(35,"mat-form-field",1)(36,"mat-label",2),t.ɵɵtext(37,"tb.rulenode.password"),t.ɵɵelementEnd(),t.ɵɵelement(38,"input",15)(39,"tb-toggle-password",16),t.ɵɵelementEnd(),t.ɵɵelementStart(40,"mat-checkbox",17),t.ɵɵtext(41),t.ɵɵpipe(42,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(43,"mat-form-field",1)(44,"mat-label",2),t.ɵɵtext(45,"tb.rulenode.connection-timeout-ms"),t.ɵɵelementEnd(),t.ɵɵelement(46,"input",18),t.ɵɵtemplate(47,il,3,3,"mat-error",10),t.ɵɵelementEnd(),t.ɵɵelementStart(48,"mat-form-field",1)(49,"mat-label",2),t.ɵɵtext(50,"tb.rulenode.handshake-timeout-ms"),t.ɵɵelementEnd(),t.ɵɵelement(51,"input",19),t.ɵɵtemplate(52,ol,3,3,"mat-error",10),t.ɵɵelementEnd(),t.ɵɵelementStart(53,"label",20),t.ɵɵtext(54,"tb.rulenode.client-properties"),t.ɵɵelementEnd(),t.ɵɵelement(55,"tb-kv-map-config-old",21),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.rabbitMqConfigForm),t.ɵɵadvance(13),t.ɵɵproperty("ngForOf",n.messageProperties),t.ɵɵadvance(6),t.ɵɵproperty("ngIf",n.rabbitMqConfigForm.get("host").hasError("required")),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.rabbitMqConfigForm.get("port").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.rabbitMqConfigForm.get("port").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.rabbitMqConfigForm.get("port").hasError("max")),t.ɵɵadvance(15),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(42,9,"tb.rulenode.automatic-recovery")," "),t.ɵɵadvance(6),t.ɵɵproperty("ngIf",n.rabbitMqConfigForm.get("connectionTimeout").hasError("min")),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.rabbitMqConfigForm.get("handshakeTimeout").hasError("min")))},dependencies:t.ɵɵgetComponentDepsFactory(ll),encapsulation:2})}}e("RabbitMqConfigComponent",ll);const sl=e=>({max:e});function pl(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.endpoint-url-pattern-required")," "))}function ml(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",20),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function dl(e,n){1&e&&(t.ɵɵelementStart(0,"mat-checkbox",21),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.use-simple-client-http-factory")," "))}function ul(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",20),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function cl(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.proxy-host-required")," "))}function fl(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.proxy-port-required")," "))}function gl(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.proxy-port-range")," "))}function hl(e,n){if(1&e&&(t.ɵɵelementStart(0,"div")(1,"div",23)(2,"mat-form-field",24)(3,"mat-label",2),t.ɵɵtext(4,"tb.rulenode.proxy-scheme"),t.ɵɵelementEnd(),t.ɵɵelementStart(5,"mat-select",25),t.ɵɵtemplate(6,ul,2,2,"mat-option",7),t.ɵɵelementEnd()(),t.ɵɵelementStart(7,"mat-form-field",26)(8,"mat-label",2),t.ɵɵtext(9,"tb.rulenode.proxy-host"),t.ɵɵelementEnd(),t.ɵɵelement(10,"input",27),t.ɵɵtemplate(11,cl,3,3,"mat-error",4),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"mat-form-field",28)(13,"mat-label",2),t.ɵɵtext(14,"tb.rulenode.proxy-port"),t.ɵɵelementEnd(),t.ɵɵelement(15,"input",29),t.ɵɵtemplate(16,fl,3,3,"mat-error",4)(17,gl,3,3,"mat-error",4),t.ɵɵelementEnd()(),t.ɵɵelementStart(18,"mat-form-field",5)(19,"mat-label",2),t.ɵɵtext(20,"tb.rulenode.proxy-user"),t.ɵɵelementEnd(),t.ɵɵelement(21,"input",30),t.ɵɵelementEnd(),t.ɵɵelementStart(22,"mat-form-field",5)(23,"mat-label",2),t.ɵɵtext(24,"tb.rulenode.proxy-password"),t.ɵɵelementEnd(),t.ɵɵelement(25,"input",31),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",e.proxySchemes),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",e.restApiCallConfigForm.get("proxyHost").hasError("required")),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",e.restApiCallConfigForm.get("proxyPort").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.restApiCallConfigForm.get("proxyPort").hasError("min")||e.restApiCallConfigForm.get("proxyPort").hasError("max"))}}function yl(e,n){if(1&e&&(t.ɵɵelementStart(0,"div")(1,"mat-checkbox",22),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(4,hl,26,4,"div",4),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,2,"tb.rulenode.use-system-proxy-properties")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",!e.restApiCallConfigForm.get("useSystemProxyProperties").value)}}function bl(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.int-range")," "))}function vl(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",1)(1,"mat-label",2),t.ɵɵtext(2,"tb.rulenode.read-timeout"),t.ɵɵelementEnd(),t.ɵɵelement(3,"input",32),t.ɵɵelementStart(4,"mat-hint",2),t.ɵɵtext(5,"tb.rulenode.read-timeout-hint"),t.ɵɵelementEnd(),t.ɵɵtemplate(6,bl,3,3,"mat-error",4),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(3),t.ɵɵproperty("max",e.IntLimit),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",e.restApiCallConfigForm.get("readTimeoutMs").hasError("max"))}}function xl(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.int-range")," "))}function Cl(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind2(2,1,"tb.rulenode.memory-buffer-size-range",t.ɵɵpureFunction1(4,sl,e.MemoryBufferSizeInKbLimit))," ")}}class Sl extends i{constructor(e,t){super(e),this.store=e,this.fb=t,this.proxySchemes=["http","https"],this.httpRequestTypes=Object.keys(Dt),this.MemoryBufferSizeInKbLimit=25e3,this.IntLimit=tn}configForm(){return this.restApiCallConfigForm}onConfigurationSet(e){this.restApiCallConfigForm=this.fb.group({restEndpointUrlPattern:[e?e.restEndpointUrlPattern:null,[N.required]],requestMethod:[e?e.requestMethod:null,[N.required]],useSimpleClientHttpFactory:[!!e&&e.useSimpleClientHttpFactory,[]],parseToPlainText:[!!e&&e.parseToPlainText,[]],ignoreRequestBody:[!!e&&e.ignoreRequestBody,[]],enableProxy:[!!e&&e.enableProxy,[]],useSystemProxyProperties:[!!e&&e.enableProxy,[]],proxyScheme:[e?e.proxyHost:null,[]],proxyHost:[e?e.proxyHost:null,[]],proxyPort:[e?e.proxyPort:null,[]],proxyUser:[e?e.proxyUser:null,[]],proxyPassword:[e?e.proxyPassword:null,[]],readTimeoutMs:[e?e.readTimeoutMs:null,[N.min(0),N.max(tn)]],maxParallelRequestsCount:[e?e.maxParallelRequestsCount:null,[N.min(0),N.max(tn)]],headers:[e?e.headers:null,[]],credentials:[e?e.credentials:null,[]],maxInMemoryBufferSizeInKb:[e?e.maxInMemoryBufferSizeInKb:null,[N.min(1),N.max(this.MemoryBufferSizeInKbLimit)]]})}validatorTriggers(){return["useSimpleClientHttpFactory","enableProxy","useSystemProxyProperties"]}updateValidators(e){const t=this.restApiCallConfigForm.get("useSimpleClientHttpFactory").value,n=this.restApiCallConfigForm.get("enableProxy").value,r=this.restApiCallConfigForm.get("useSystemProxyProperties").value;n&&!r?(this.restApiCallConfigForm.get("proxyHost").setValidators(n?[N.required]:[]),this.restApiCallConfigForm.get("proxyPort").setValidators(n?[N.required,N.min(1),N.max(65535)]:[])):(this.restApiCallConfigForm.get("proxyHost").setValidators([]),this.restApiCallConfigForm.get("proxyPort").setValidators([]),t?this.restApiCallConfigForm.get("readTimeoutMs").setValidators([]):this.restApiCallConfigForm.get("readTimeoutMs").setValidators([N.min(0),N.max(tn)])),this.restApiCallConfigForm.get("readTimeoutMs").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("proxyHost").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("proxyPort").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("credentials").updateValueAndValidity({emitEvent:e})}static{this.ɵfac=function(e){return new(e||Sl)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Sl,selectors:[["tb-external-node-rest-api-call-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:48,vars:26,consts:[[1,"flex","flex-col",3,"formGroup"],["subscriptSizing","dynamic",1,"mat-block"],["translate",""],["required","","matInput","","formControlName","restEndpointUrlPattern"],[4,"ngIf"],[1,"mat-block"],["formControlName","requestMethod"],[3,"value",4,"ngFor","ngForOf"],["formControlName","enableProxy"],["formControlName","useSimpleClientHttpFactory",4,"ngIf"],["formControlName","parseToPlainText"],["translate","",1,"tb-hint",2,"padding-bottom","5px"],["formControlName","ignoreRequestBody"],["class","mat-block","subscriptSizing","dynamic",4,"ngIf"],["type","text","min","0","inputmode","numeric","pattern","[0-9]*","matInput","","formControlName","maxParallelRequestsCount",3,"max"],["type","text","min","1","inputmode","numeric","pattern","[0-9]*","matInput","","formControlName","maxInMemoryBufferSizeInKb",3,"max"],["translate","",1,"tb-title"],[1,"tb-hint",3,"innerHTML"],["required","false","formControlName","headers","keyText","tb.rulenode.header","keyRequiredText","tb.rulenode.header-required","valText","tb.rulenode.value","valRequiredText","tb.rulenode.value-required"],["formControlName","credentials",3,"disableCertPemCredentials"],[3,"value"],["formControlName","useSimpleClientHttpFactory"],["formControlName","useSystemProxyProperties"],[1,"gt-sm:flex","gt-sm:flex-row","gt-sm:gap-2"],[1,"mat-block","gt-sm:max-w-10%","gt-sm:flex-full"],["formControlName","proxyScheme"],[1,"md-block","gt-sm:max-w-50%","gt-sm:flex-full"],["matInput","","required","","formControlName","proxyHost"],[1,"mat-block","gt-sm:max-w-40%","gt-sm:flex-full"],["matInput","","required","","formControlName","proxyPort","type","number","step","1"],["matInput","","formControlName","proxyUser"],["matInput","","formControlName","proxyPassword"],["type","text","min","0","inputmode","numeric","pattern","[0-9]*","matInput","","formControlName","readTimeoutMs",3,"max"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"mat-form-field",1)(2,"mat-label",2),t.ɵɵtext(3,"tb.rulenode.endpoint-url-pattern"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",3),t.ɵɵtemplate(5,pl,3,3,"mat-error",4),t.ɵɵelementStart(6,"mat-hint",2),t.ɵɵtext(7,"tb.rulenode.general-pattern-hint"),t.ɵɵelementEnd()(),t.ɵɵelementStart(8,"mat-form-field",5)(9,"mat-label",2),t.ɵɵtext(10,"tb.rulenode.request-method"),t.ɵɵelementEnd(),t.ɵɵelementStart(11,"mat-select",6),t.ɵɵtemplate(12,ml,2,2,"mat-option",7),t.ɵɵelementEnd()(),t.ɵɵelementStart(13,"mat-checkbox",8),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(16,dl,3,3,"mat-checkbox",9),t.ɵɵelementStart(17,"mat-checkbox",10),t.ɵɵtext(18),t.ɵɵpipe(19,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(20,"div",11),t.ɵɵtext(21,"tb.rulenode.parse-to-plain-text-hint"),t.ɵɵelementEnd(),t.ɵɵelementStart(22,"mat-checkbox",12),t.ɵɵtext(23),t.ɵɵpipe(24,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(25,yl,5,4,"div",4)(26,vl,7,2,"mat-form-field",13),t.ɵɵelementStart(27,"mat-form-field",1)(28,"mat-label",2),t.ɵɵtext(29,"tb.rulenode.max-parallel-requests-count"),t.ɵɵelementEnd(),t.ɵɵelement(30,"input",14),t.ɵɵelementStart(31,"mat-hint",2),t.ɵɵtext(32,"tb.rulenode.max-parallel-requests-count-hint"),t.ɵɵelementEnd(),t.ɵɵtemplate(33,xl,3,3,"mat-error",4),t.ɵɵelementEnd(),t.ɵɵelementStart(34,"mat-form-field",1)(35,"mat-label",2),t.ɵɵtext(36,"tb.rulenode.max-response-size"),t.ɵɵelementEnd(),t.ɵɵelement(37,"input",15),t.ɵɵelementStart(38,"mat-hint",2),t.ɵɵtext(39,"tb.rulenode.max-response-size-hint"),t.ɵɵelementEnd(),t.ɵɵtemplate(40,Cl,3,6,"mat-error",4),t.ɵɵelementEnd(),t.ɵɵelementStart(41,"label",16),t.ɵɵtext(42,"tb.rulenode.headers"),t.ɵɵelementEnd(),t.ɵɵelement(43,"div",17),t.ɵɵpipe(44,"translate"),t.ɵɵpipe(45,"safe"),t.ɵɵelement(46,"tb-kv-map-config-old",18)(47,"tb-credentials-config",19),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.restApiCallConfigForm),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.restApiCallConfigForm.get("restEndpointUrlPattern").hasError("required")),t.ɵɵadvance(7),t.ɵɵproperty("ngForOf",n.httpRequestTypes),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(15,15,"tb.rulenode.enable-proxy")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",!n.restApiCallConfigForm.get("enableProxy").value),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(19,17,"tb.rulenode.parse-to-plain-text")," "),t.ɵɵadvance(5),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(24,19,"tb.rulenode.ignore-request-body")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.restApiCallConfigForm.get("enableProxy").value),t.ɵɵadvance(),t.ɵɵproperty("ngIf",!n.restApiCallConfigForm.get("useSimpleClientHttpFactory").value||n.restApiCallConfigForm.get("enableProxy").value),t.ɵɵadvance(4),t.ɵɵproperty("max",n.IntLimit),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.restApiCallConfigForm.get("maxParallelRequestsCount").hasError("max")),t.ɵɵadvance(4),t.ɵɵproperty("max",n.MemoryBufferSizeInKbLimit),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.restApiCallConfigForm.get("maxInMemoryBufferSizeInKb").hasError("min")||n.restApiCallConfigForm.get("maxInMemoryBufferSizeInKb").hasError("max")),t.ɵɵadvance(3),t.ɵɵproperty("innerHTML",t.ɵɵpipeBind2(45,23,t.ɵɵpipeBind1(44,21,"tb.rulenode.headers-hint"),"html"),t.ɵɵsanitizeHtml),t.ɵɵadvance(4),t.ɵɵproperty("disableCertPemCredentials",n.restApiCallConfigForm.get("useSimpleClientHttpFactory").value))},dependencies:t.ɵɵgetComponentDepsFactory(Sl),encapsulation:2})}}function Tl(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",22),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.toUpperCase()," ")}}function Il(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.smtp-host-required")," "))}function El(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.smtp-port-required")," "))}function Fl(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.smtp-port-range")," "))}function ql(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.smtp-port-range")," "))}function Al(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.timeout-required")," "))}function kl(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.min-timeout-msec-message")," "))}function Nl(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",22),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e," ")}}function wl(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",4)(1,"mat-label",5),t.ɵɵtext(2,"tb.rulenode.tls-version"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"mat-select",23),t.ɵɵtemplate(4,Nl,2,2,"mat-option",7),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(4),t.ɵɵproperty("ngForOf",e.tlsVersions)}}function Ml(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.proxy-host-required")," "))}function Bl(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.proxy-port-required")," "))}function Vl(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.proxy-port-range")," "))}function Ol(e,n){if(1&e&&(t.ɵɵelementStart(0,"div")(1,"div",8)(2,"mat-form-field",9)(3,"mat-label",5),t.ɵɵtext(4,"tb.rulenode.proxy-host"),t.ɵɵelementEnd(),t.ɵɵelement(5,"input",24),t.ɵɵtemplate(6,Ml,3,3,"mat-error",11),t.ɵɵelementEnd(),t.ɵɵelementStart(7,"mat-form-field",12)(8,"mat-label",5),t.ɵɵtext(9,"tb.rulenode.proxy-port"),t.ɵɵelementEnd(),t.ɵɵelement(10,"input",25),t.ɵɵtemplate(11,Bl,3,3,"mat-error",11)(12,Vl,3,3,"mat-error",11),t.ɵɵelementEnd()(),t.ɵɵelementStart(13,"mat-form-field",4)(14,"mat-label",5),t.ɵɵtext(15,"tb.rulenode.proxy-user"),t.ɵɵelementEnd(),t.ɵɵelement(16,"input",26),t.ɵɵelementEnd(),t.ɵɵelementStart(17,"mat-form-field",4)(18,"mat-label",5),t.ɵɵtext(19,"tb.rulenode.proxy-password"),t.ɵɵelementEnd(),t.ɵɵelement(20,"input",27),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext(2);t.ɵɵadvance(6),t.ɵɵproperty("ngIf",e.sendEmailConfigForm.get("proxyHost").hasError("required")),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",e.sendEmailConfigForm.get("proxyPort").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.sendEmailConfigForm.get("proxyPort").hasError("min")||e.sendEmailConfigForm.get("proxyPort").hasError("max"))}}function Dl(e,n){if(1&e&&(t.ɵɵelementStart(0,"section",3)(1,"mat-form-field",4)(2,"mat-label",5),t.ɵɵtext(3,"tb.rulenode.smtp-protocol"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-select",6),t.ɵɵtemplate(5,Tl,2,2,"mat-option",7),t.ɵɵelementEnd()(),t.ɵɵelementStart(6,"div",8)(7,"mat-form-field",9)(8,"mat-label",5),t.ɵɵtext(9,"tb.rulenode.smtp-host"),t.ɵɵelementEnd(),t.ɵɵelement(10,"input",10),t.ɵɵtemplate(11,Il,3,3,"mat-error",11),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"mat-form-field",12)(13,"mat-label",5),t.ɵɵtext(14,"tb.rulenode.smtp-port"),t.ɵɵelementEnd(),t.ɵɵelement(15,"input",13),t.ɵɵtemplate(16,El,3,3,"mat-error",11)(17,Fl,3,3,"mat-error",11)(18,ql,3,3,"mat-error",11),t.ɵɵelementEnd()(),t.ɵɵelementStart(19,"mat-form-field",4)(20,"mat-label",5),t.ɵɵtext(21,"tb.rulenode.timeout-msec"),t.ɵɵelementEnd(),t.ɵɵelement(22,"input",14),t.ɵɵtemplate(23,Al,3,3,"mat-error",11)(24,kl,3,3,"mat-error",11),t.ɵɵelementEnd(),t.ɵɵelementStart(25,"mat-checkbox",15),t.ɵɵtext(26),t.ɵɵpipe(27,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(28,wl,5,1,"mat-form-field",16),t.ɵɵelementStart(29,"tb-checkbox",17),t.ɵɵtext(30),t.ɵɵpipe(31,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(32,Ol,21,3,"div",11),t.ɵɵelementStart(33,"mat-form-field",18)(34,"mat-label",5),t.ɵɵtext(35,"tb.rulenode.username"),t.ɵɵelementEnd(),t.ɵɵelement(36,"input",19),t.ɵɵpipe(37,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(38,"mat-form-field",18)(39,"mat-label",5),t.ɵɵtext(40,"tb.rulenode.password"),t.ɵɵelementEnd(),t.ɵɵelement(41,"input",20),t.ɵɵpipe(42,"translate"),t.ɵɵelement(43,"tb-toggle-password",21),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",e.smtpProtocols),t.ɵɵadvance(6),t.ɵɵproperty("ngIf",e.sendEmailConfigForm.get("smtpHost").hasError("required")),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",e.sendEmailConfigForm.get("smtpPort").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.sendEmailConfigForm.get("smtpPort").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.sendEmailConfigForm.get("smtpPort").hasError("max")),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",e.sendEmailConfigForm.get("timeout").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.sendEmailConfigForm.get("timeout").hasError("min")),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(27,13,"tb.rulenode.enable-tls")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",!0===e.sendEmailConfigForm.get("enableTls").value),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(31,15,"tb.rulenode.enable-proxy")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.sendEmailConfigForm.get("enableProxy").value),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(37,17,"tb.rulenode.enter-username")),t.ɵɵadvance(5),t.ɵɵpropertyInterpolate("placeholder",t.ɵɵpipeBind1(42,19,"tb.rulenode.enter-password"))}}e("RestApiCallConfigComponent",Sl);class Ll extends i{constructor(e,t){super(e),this.store=e,this.fb=t,this.smtpProtocols=["smtp","smtps"],this.tlsVersions=["TLSv1","TLSv1.1","TLSv1.2","TLSv1.3"]}configForm(){return this.sendEmailConfigForm}onConfigurationSet(e){this.sendEmailConfigForm=this.fb.group({useSystemSmtpSettings:[!!e&&e.useSystemSmtpSettings,[]],smtpProtocol:[e?e.smtpProtocol:null,[]],smtpHost:[e?e.smtpHost:null,[]],smtpPort:[e?e.smtpPort:null,[]],timeout:[e?e.timeout:null,[]],enableTls:[!!e&&e.enableTls,[]],tlsVersion:[e?e.tlsVersion:null,[]],enableProxy:[!!e&&e.enableProxy,[]],proxyHost:[e?e.proxyHost:null,[]],proxyPort:[e?e.proxyPort:null,[]],proxyUser:[e?e.proxyUser:null,[]],proxyPassword:[e?e.proxyPassword:null,[]],username:[e?e.username:null,[]],password:[e?e.password:null,[]]})}validatorTriggers(){return["useSystemSmtpSettings","enableProxy"]}updateValidators(e){const t=this.sendEmailConfigForm.get("useSystemSmtpSettings").value,n=this.sendEmailConfigForm.get("enableProxy").value;t?(this.sendEmailConfigForm.get("smtpProtocol").setValidators([]),this.sendEmailConfigForm.get("smtpHost").setValidators([]),this.sendEmailConfigForm.get("smtpPort").setValidators([]),this.sendEmailConfigForm.get("timeout").setValidators([]),this.sendEmailConfigForm.get("proxyHost").setValidators([]),this.sendEmailConfigForm.get("proxyPort").setValidators([])):(this.sendEmailConfigForm.get("smtpProtocol").setValidators([N.required]),this.sendEmailConfigForm.get("smtpHost").setValidators([N.required]),this.sendEmailConfigForm.get("smtpPort").setValidators([N.required,N.min(1),N.max(65535)]),this.sendEmailConfigForm.get("timeout").setValidators([N.required,N.min(0)]),this.sendEmailConfigForm.get("proxyHost").setValidators(n?[N.required]:[]),this.sendEmailConfigForm.get("proxyPort").setValidators(n?[N.required,N.min(1),N.max(65535)]:[])),this.sendEmailConfigForm.get("smtpProtocol").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("smtpHost").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("smtpPort").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("timeout").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("proxyHost").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("proxyPort").updateValueAndValidity({emitEvent:e})}static{this.ɵfac=function(e){return new(e||Ll)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Ll,selectors:[["tb-external-node-send-email-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:5,vars:5,consts:[[1,"flex","flex-col",3,"formGroup"],["formControlName","useSystemSmtpSettings"],["class","flex flex-col",4,"ngIf"],[1,"flex","flex-col"],[1,"mat-block"],["translate",""],["formControlName","smtpProtocol"],[3,"value",4,"ngFor","ngForOf"],[1,"gt-sm:flex","gt-sm:flex-row","gt-sm:gap-2"],[1,"mat-block","gt-sm:max-w-60%","gt-sm:flex-full"],["required","","matInput","","formControlName","smtpHost"],[4,"ngIf"],[1,"mat-block","gt-sm:max-w-40%","gt-sm:flex-full"],["required","","type","number","step","1","min","1","max","65535","matInput","","formControlName","smtpPort"],["required","","type","number","step","1","min","0","matInput","","formControlName","timeout"],["formControlName","enableTls"],["class","mat-block",4,"ngIf"],["formControlName","enableProxy"],["floatLabel","always",1,"mat-block"],["matInput","","formControlName","username",3,"placeholder"],["matInput","","type","password","formControlName","password",3,"placeholder"],["matSuffix",""],[3,"value"],["formControlName","tlsVersion"],["matInput","","required","","formControlName","proxyHost"],["matInput","","required","","formControlName","proxyPort","type","number","step","1","min","1","max","65535"],["matInput","","formControlName","proxyUser"],["matInput","","formControlName","proxyPassword"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"mat-checkbox",1),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(4,Dl,44,21,"section",2),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.sendEmailConfigForm),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,3,"tb.rulenode.use-system-smtp-settings")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",!1===n.sendEmailConfigForm.get("useSystemSmtpSettings").value))},dependencies:t.ɵɵgetComponentDepsFactory(Ll),encapsulation:2})}}function Pl(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.numbers-to-template-required")," "))}function Rl(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.sms-message-template-required")," "))}function _l(e,n){1&e&&t.ɵɵelement(0,"tb-sms-provider-configuration",9)}e("SendEmailConfigComponent",Ll);class jl extends i{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.sendSmsConfigForm}onConfigurationSet(e){this.sendSmsConfigForm=this.fb.group({numbersToTemplate:[e?e.numbersToTemplate:null,[N.required]],smsMessageTemplate:[e?e.smsMessageTemplate:null,[N.required]],useSystemSmsSettings:[!!e&&e.useSystemSmsSettings,[]],smsProviderConfiguration:[e?e.smsProviderConfiguration:null,[]]})}validatorTriggers(){return["useSystemSmsSettings"]}updateValidators(e){this.sendSmsConfigForm.get("useSystemSmsSettings").value?this.sendSmsConfigForm.get("smsProviderConfiguration").setValidators([]):this.sendSmsConfigForm.get("smsProviderConfiguration").setValidators([N.required]),this.sendSmsConfigForm.get("smsProviderConfiguration").updateValueAndValidity({emitEvent:e})}static{this.ɵfac=function(e){return new(e||jl)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:jl,selectors:[["tb-external-node-send-sms-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:20,vars:13,consts:[[1,"flex","flex-col",3,"formGroup"],["subscriptSizing","dynamic",1,"mat-block"],["translate",""],["required","","matInput","","formControlName","numbersToTemplate"],[4,"ngIf"],[3,"innerHTML"],["required","","matInput","","formControlName","smsMessageTemplate","rows","6"],["formControlName","useSystemSmsSettings"],["formControlName","smsProviderConfiguration","required","",4,"ngIf"],["formControlName","smsProviderConfiguration","required",""]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"mat-form-field",1)(2,"mat-label",2),t.ɵɵtext(3,"tb.rulenode.numbers-to-template"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",3),t.ɵɵtemplate(5,Pl,3,3,"mat-error",4),t.ɵɵelement(6,"mat-hint",5),t.ɵɵpipe(7,"translate"),t.ɵɵpipe(8,"safe"),t.ɵɵelementEnd(),t.ɵɵelementStart(9,"mat-form-field",1)(10,"mat-label",2),t.ɵɵtext(11,"tb.rulenode.sms-message-template"),t.ɵɵelementEnd(),t.ɵɵelement(12,"textarea",6),t.ɵɵtemplate(13,Rl,3,3,"mat-error",4),t.ɵɵelementStart(14,"mat-hint",2),t.ɵɵtext(15,"tb.rulenode.general-pattern-hint"),t.ɵɵelementEnd()(),t.ɵɵelementStart(16,"mat-checkbox",7),t.ɵɵtext(17),t.ɵɵpipe(18,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(19,_l,1,0,"tb-sms-provider-configuration",8),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.sendSmsConfigForm),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.sendSmsConfigForm.get("numbersToTemplate").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("innerHTML",t.ɵɵpipeBind2(8,8,t.ɵɵpipeBind1(7,6,"tb.rulenode.numbers-to-template-hint"),"html"),t.ɵɵsanitizeHtml),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",n.sendSmsConfigForm.get("smsMessageTemplate").hasError("required")),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(18,11,"tb.rulenode.use-system-sms-settings")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",!1===n.sendSmsConfigForm.get("useSystemSmsSettings").value))},dependencies:t.ɵɵgetComponentDepsFactory(jl),encapsulation:2})}}function Gl(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.message-template-required")," "))}function Kl(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.slack-api-token-required")," "))}function Ul(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",11)(1,"mat-label",2),t.ɵɵtext(2,"tb.rulenode.slack-api-token"),t.ɵɵelementEnd(),t.ɵɵelement(3,"input",12),t.ɵɵtemplate(4,Kl,3,3,"mat-error",4),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(4),t.ɵɵproperty("ngIf",e.slackConfigForm.get("botToken").hasError("required"))}}function Hl(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-radio-button",13),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.slackChanelTypesTranslateMap.get(e))," ")}}e("SendSmsConfigComponent",jl);class zl extends i{constructor(e,t){super(e),this.store=e,this.fb=t,this.slackChanelTypes=Object.keys(F),this.slackChanelTypesTranslateMap=q}configForm(){return this.slackConfigForm}onConfigurationSet(e){this.slackConfigForm=this.fb.group({botToken:[e?e.botToken:null],useSystemSettings:[!!e&&e.useSystemSettings],messageTemplate:[e?e.messageTemplate:null,[N.required]],conversationType:[e?e.conversationType:null,[N.required]],conversation:[e?e.conversation:null,[N.required]]})}validatorTriggers(){return["useSystemSettings"]}updateValidators(e){this.slackConfigForm.get("useSystemSettings").value?this.slackConfigForm.get("botToken").clearValidators():this.slackConfigForm.get("botToken").setValidators([N.required]),this.slackConfigForm.get("botToken").updateValueAndValidity({emitEvent:e})}static{this.ɵfac=function(e){return new(e||zl)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:zl,selectors:[["tb-external-node-slack-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:18,vars:12,consts:[[1,"flex","flex-col",3,"formGroup"],[1,"mat-block","flex-1"],["translate",""],["required","","matInput","","formControlName","messageTemplate"],[4,"ngIf"],["formControlName","useSystemSettings"],["class","mat-block",4,"ngIf"],[1,"tb-title"],["formControlName","conversationType"],[3,"value",4,"ngFor","ngForOf"],["formControlName","conversation","required","",3,"token","slackChanelType"],[1,"mat-block"],["required","","matInput","","formControlName","botToken"],[3,"value"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"mat-form-field",1)(2,"mat-label",2),t.ɵɵtext(3,"tb.rulenode.message-template"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",3),t.ɵɵtemplate(5,Gl,3,3,"mat-error",4),t.ɵɵelementStart(6,"mat-hint",2),t.ɵɵtext(7,"tb.rulenode.general-pattern-hint"),t.ɵɵelementEnd()(),t.ɵɵelementStart(8,"mat-checkbox",5),t.ɵɵtext(9),t.ɵɵpipe(10,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(11,Ul,5,1,"mat-form-field",6),t.ɵɵelementStart(12,"label",7),t.ɵɵtext(13),t.ɵɵpipe(14,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(15,"mat-radio-group",8),t.ɵɵtemplate(16,Hl,3,4,"mat-radio-button",9),t.ɵɵelementEnd(),t.ɵɵelement(17,"tb-slack-conversation-autocomplete",10),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.slackConfigForm),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.slackConfigForm.get("messageTemplate").hasError("required")),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(10,8,"tb.rulenode.use-system-slack-settings")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",!n.slackConfigForm.get("useSystemSettings").value),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(14,10,"notification.slack-chanel-type")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.slackChanelTypes),t.ɵɵadvance(),t.ɵɵproperty("token",n.slackConfigForm.get("useSystemSettings").value?"":n.slackConfigForm.get("botToken").value)("slackChanelType",n.slackConfigForm.get("conversationType").value))},dependencies:t.ɵɵgetComponentDepsFactory(zl),styles:["[_nghost-%COMP%] .tb-title[_ngcontent-%COMP%]{display:block;padding-bottom:6px}[_nghost-%COMP%] .mat-mdc-radio-group{display:flex;flex-direction:row;margin-bottom:22px;gap:12px}[_nghost-%COMP%] .mat-mdc-radio-group .mat-mdc-radio-button{flex:1 1 100%;padding:4px;border:1px solid rgba(0,0,0,.12);border-radius:6px}@media screen and (max-width: 599px){[_nghost-%COMP%] .mat-mdc-radio-group{flex-direction:column}}"]})}}function $l(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.topic-arn-pattern-required")," "))}function Ql(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.aws-access-key-id-required")," "))}function Jl(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.aws-secret-access-key-required")," "))}function Yl(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.aws-region-required")," "))}e("SlackConfigComponent",zl);class Wl extends i{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.snsConfigForm}onConfigurationSet(e){this.snsConfigForm=this.fb.group({topicArnPattern:[e?e.topicArnPattern:null,[N.required]],accessKeyId:[e?e.accessKeyId:null,[N.required]],secretAccessKey:[e?e.secretAccessKey:null,[N.required]],region:[e?e.region:null,[N.required]]})}static{this.ɵfac=function(e){return new(e||Wl)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Wl,selectors:[["tb-external-node-sns-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:23,vars:5,consts:[[1,"flex","flex-col",3,"formGroup"],["subscriptSizing","dynamic",1,"mat-block"],["translate",""],["required","","matInput","","formControlName","topicArnPattern"],[4,"ngIf"],[1,"mat-block"],["required","","matInput","","formControlName","accessKeyId"],["required","","matInput","","formControlName","secretAccessKey"],["required","","matInput","","formControlName","region"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"mat-form-field",1)(2,"mat-label",2),t.ɵɵtext(3,"tb.rulenode.topic-arn-pattern"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",3),t.ɵɵtemplate(5,$l,3,3,"mat-error",4),t.ɵɵelementStart(6,"mat-hint",2),t.ɵɵtext(7,"tb.rulenode.general-pattern-hint"),t.ɵɵelementEnd()(),t.ɵɵelementStart(8,"mat-form-field",5)(9,"mat-label",2),t.ɵɵtext(10,"tb.rulenode.aws-access-key-id"),t.ɵɵelementEnd(),t.ɵɵelement(11,"input",6),t.ɵɵtemplate(12,Ql,3,3,"mat-error",4),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"mat-form-field",5)(14,"mat-label",2),t.ɵɵtext(15,"tb.rulenode.aws-secret-access-key"),t.ɵɵelementEnd(),t.ɵɵelement(16,"input",7),t.ɵɵtemplate(17,Jl,3,3,"mat-error",4),t.ɵɵelementEnd(),t.ɵɵelementStart(18,"mat-form-field",5)(19,"mat-label",2),t.ɵɵtext(20,"tb.rulenode.aws-region"),t.ɵɵelementEnd(),t.ɵɵelement(21,"input",8),t.ɵɵtemplate(22,Yl,3,3,"mat-error",4),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.snsConfigForm),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.snsConfigForm.get("topicArnPattern").hasError("required")),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",n.snsConfigForm.get("accessKeyId").hasError("required")),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.snsConfigForm.get("secretAccessKey").hasError("required")),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.snsConfigForm.get("region").hasError("required")))},dependencies:t.ɵɵgetComponentDepsFactory(Wl),encapsulation:2})}}function Xl(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",15),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.sqsQueueTypeTranslationsMap.get(e))," ")}}function Zl(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.queue-url-pattern-required")," "))}function es(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.min-delay-seconds-message")," "))}function ts(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.max-delay-seconds-message")," "))}function ns(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",1)(1,"mat-label",2),t.ɵɵtext(2,"tb.rulenode.delay-seconds"),t.ɵɵelementEnd(),t.ɵɵelement(3,"input",16),t.ɵɵtemplate(4,es,3,3,"mat-error",7)(5,ts,3,3,"mat-error",7),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(4),t.ɵɵproperty("ngIf",e.sqsConfigForm.get("delaySeconds").hasError("min")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.sqsConfigForm.get("delaySeconds").hasError("max"))}}function rs(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.aws-access-key-id-required")," "))}function as(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.aws-secret-access-key-required")," "))}function is(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.aws-region-required")," "))}e("SnsConfigComponent",Wl);class os extends i{constructor(e,t){super(e),this.store=e,this.fb=t,this.sqsQueueType=Nt,this.sqsQueueTypes=Object.keys(Nt),this.sqsQueueTypeTranslationsMap=wt}configForm(){return this.sqsConfigForm}onConfigurationSet(e){this.sqsConfigForm=this.fb.group({queueType:[e?e.queueType:null,[N.required]],queueUrlPattern:[e?e.queueUrlPattern:null,[N.required]],delaySeconds:[e?e.delaySeconds:null,[N.min(0),N.max(900)]],messageAttributes:[e?e.messageAttributes:null,[]],accessKeyId:[e?e.accessKeyId:null,[N.required]],secretAccessKey:[e?e.secretAccessKey:null,[N.required]],region:[e?e.region:null,[N.required]]})}static{this.ɵfac=function(e){return new(e||os)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:os,selectors:[["tb-external-node-sqs-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:35,vars:13,consts:[[1,"flex","flex-col",3,"formGroup"],[1,"mat-block"],["translate",""],["formControlName","queueType","required",""],[3,"value",4,"ngFor","ngForOf"],["subscriptSizing","dynamic",1,"mat-block"],["required","","matInput","","formControlName","queueUrlPattern"],[4,"ngIf"],["class","mat-block",4,"ngIf"],["translate","",1,"tb-title"],[1,"tb-hint",3,"innerHTML"],["required","false","formControlName","messageAttributes","keyText","tb.rulenode.name","keyRequiredText","tb.rulenode.name-required","valText","tb.rulenode.value","valRequiredText","tb.rulenode.value-required"],["required","","matInput","","formControlName","accessKeyId"],["required","","matInput","","formControlName","secretAccessKey"],["required","","matInput","","formControlName","region"],[3,"value"],["required","","type","number","min","0","max","900","step","1","matInput","","formControlName","delaySeconds"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"mat-form-field",1)(2,"mat-label",2),t.ɵɵtext(3,"tb.rulenode.queue-type"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-select",3),t.ɵɵtemplate(5,Xl,3,4,"mat-option",4),t.ɵɵelementEnd()(),t.ɵɵelementStart(6,"mat-form-field",5)(7,"mat-label",2),t.ɵɵtext(8,"tb.rulenode.queue-url-pattern"),t.ɵɵelementEnd(),t.ɵɵelement(9,"input",6),t.ɵɵtemplate(10,Zl,3,3,"mat-error",7),t.ɵɵelementStart(11,"mat-hint",2),t.ɵɵtext(12,"tb.rulenode.general-pattern-hint"),t.ɵɵelementEnd()(),t.ɵɵtemplate(13,ns,6,2,"mat-form-field",8),t.ɵɵelementStart(14,"label",9),t.ɵɵtext(15,"tb.rulenode.message-attributes"),t.ɵɵelementEnd(),t.ɵɵelement(16,"div",10),t.ɵɵpipe(17,"translate"),t.ɵɵpipe(18,"safe"),t.ɵɵelement(19,"tb-kv-map-config-old",11),t.ɵɵelementStart(20,"mat-form-field",1)(21,"mat-label",2),t.ɵɵtext(22,"tb.rulenode.aws-access-key-id"),t.ɵɵelementEnd(),t.ɵɵelement(23,"input",12),t.ɵɵtemplate(24,rs,3,3,"mat-error",7),t.ɵɵelementEnd(),t.ɵɵelementStart(25,"mat-form-field",1)(26,"mat-label",2),t.ɵɵtext(27,"tb.rulenode.aws-secret-access-key"),t.ɵɵelementEnd(),t.ɵɵelement(28,"input",13),t.ɵɵtemplate(29,as,3,3,"mat-error",7),t.ɵɵelementEnd(),t.ɵɵelementStart(30,"mat-form-field",1)(31,"mat-label",2),t.ɵɵtext(32,"tb.rulenode.aws-region"),t.ɵɵelementEnd(),t.ɵɵelement(33,"input",14),t.ɵɵtemplate(34,is,3,3,"mat-error",7),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.sqsConfigForm),t.ɵɵadvance(5),t.ɵɵproperty("ngForOf",n.sqsQueueTypes),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.sqsConfigForm.get("queueUrlPattern").hasError("required")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.sqsConfigForm.get("queueType").value===n.sqsQueueType.STANDARD),t.ɵɵadvance(3),t.ɵɵproperty("innerHTML",t.ɵɵpipeBind2(18,10,t.ɵɵpipeBind1(17,8,"tb.rulenode.message-attributes-hint"),"html"),t.ɵɵsanitizeHtml),t.ɵɵadvance(8),t.ɵɵproperty("ngIf",n.sqsConfigForm.get("accessKeyId").hasError("required")),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.sqsConfigForm.get("secretAccessKey").hasError("required")),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.sqsConfigForm.get("region").hasError("required")))},dependencies:t.ɵɵgetComponentDepsFactory(os),encapsulation:2})}}function ls(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.function-name-required")," "))}function ss(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.aws-access-key-id-required")," "))}function ps(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.aws-secret-access-key-required")," "))}function ms(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.aws-region-required")," "))}function ds(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.connection-timeout-required")," "))}function us(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.connection-timeout-min")," "))}function cs(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.request-timeout-required")," "))}function fs(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.request-timeout-min")," "))}e("SqsConfigComponent",os);class gs extends i{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.lambdaConfigForm}onConfigurationSet(e){this.lambdaConfigForm=this.fb.group({functionName:[e?e.functionName:null,[N.required]],qualifier:[e?e.qualifier:null,[]],accessKey:[e?e.accessKey:null,[N.required]],secretKey:[e?e.secretKey:null,[N.required]],region:[e?e.region:null,[N.required]],connectionTimeout:[e?e.connectionTimeout:null,[N.required,N.min(0)]],requestTimeout:[e?e.requestTimeout:null,[N.required,N.min(0)]],tellFailureIfFuncThrowsExc:[!!e&&e.tellFailureIfFuncThrowsExc,[]]})}static{this.ɵfac=function(e){return new(e||gs)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:gs,selectors:[["tb-external-node-lambda-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:71,vars:28,consts:[[1,"tb-form-panel","no-padding","no-border",3,"formGroup"],[1,"tb-form-panel","stroked"],[1,"tb-form-row","no-padding","no-border"],["translate","",1,"tb-form-panel-title","tb-required"],["popupHelpLink","rulenode/node-templatization-doc",3,"hintText"],[1,"tb-standard-fields"],[1,"mat-block"],["required","","matInput","","formControlName","functionName"],[4,"ngIf"],["matInput","","formControlName","qualifier"],["translate",""],["expanded","",1,"tb-settings"],["required","","matInput","","formControlName","accessKey"],["required","","matInput","","formControlName","secretKey"],["required","","matInput","","formControlName","region"],[1,"tb-form-panel","stroked","no-padding"],[1,"tb-settings"],[2,"padding","16px"],[1,"tb-form-panel","no-border","no-padding","no-gap",2,"margin-top","0"],[1,"tb-form-row","no-border","same-padding","tb-standard-fields"],[1,"flex"],["type","number","required","","min","0","matInput","","formControlName","connectionTimeout"],["matSuffix","","aria-hidden","false","aria-label","help-icon","color","primary",1,"help-icon","margin-8","cursor-pointer",3,"matTooltip"],["type","number","required","","min","0","matInput","","formControlName","requestTimeout"],[1,"tb-form-row","no-border",2,"margin-bottom","16px",3,"tb-hint-tooltip-icon"],["formControlName","tellFailureIfFuncThrowsExc",1,"mat-slide"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1)(2,"div",2)(3,"div",3),t.ɵɵtext(4,"tb.rulenode.function-configuration"),t.ɵɵelementEnd()(),t.ɵɵelement(5,"tb-example-hint",4),t.ɵɵelementStart(6,"div",5)(7,"mat-form-field",6)(8,"mat-label"),t.ɵɵtext(9),t.ɵɵpipe(10,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(11,"input",7),t.ɵɵtemplate(12,ls,3,3,"mat-error",8),t.ɵɵelementEnd(),t.ɵɵelementStart(13,"mat-form-field",6)(14,"mat-label"),t.ɵɵtext(15),t.ɵɵpipe(16,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(17,"input",9),t.ɵɵelementStart(18,"mat-hint",10),t.ɵɵtext(19,"tb.rulenode.qualifier-hint"),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(20,"div",1)(21,"mat-expansion-panel",11)(22,"mat-expansion-panel-header")(23,"mat-panel-title",3),t.ɵɵtext(24,"tb.rulenode.aws-credentials"),t.ɵɵelementEnd()(),t.ɵɵelementStart(25,"div",5)(26,"mat-form-field",6)(27,"mat-label",10),t.ɵɵtext(28,"tb.rulenode.aws-access-key-id"),t.ɵɵelementEnd(),t.ɵɵelement(29,"input",12),t.ɵɵtemplate(30,ss,3,3,"mat-error",8),t.ɵɵelementEnd(),t.ɵɵelementStart(31,"mat-form-field",6)(32,"mat-label",10),t.ɵɵtext(33,"tb.rulenode.aws-secret-access-key"),t.ɵɵelementEnd(),t.ɵɵelement(34,"input",13),t.ɵɵtemplate(35,ps,3,3,"mat-error",8),t.ɵɵelementEnd(),t.ɵɵelementStart(36,"mat-form-field",6)(37,"mat-label",10),t.ɵɵtext(38,"tb.rulenode.aws-region"),t.ɵɵelementEnd(),t.ɵɵelement(39,"input",14),t.ɵɵtemplate(40,ms,3,3,"mat-error",8),t.ɵɵelementEnd()()()(),t.ɵɵelementStart(41,"div",15)(42,"mat-expansion-panel",16)(43,"mat-expansion-panel-header",17)(44,"mat-panel-title",10),t.ɵɵtext(45,"tb.rulenode.advanced-settings"),t.ɵɵelementEnd()(),t.ɵɵelementStart(46,"div",18)(47,"div",19)(48,"mat-form-field",20)(49,"mat-label",10),t.ɵɵtext(50,"tb.rulenode.connection-timeout"),t.ɵɵelementEnd(),t.ɵɵelement(51,"input",21),t.ɵɵtemplate(52,ds,3,3,"mat-error",8)(53,us,3,3,"mat-error",8),t.ɵɵelementStart(54,"mat-icon",22),t.ɵɵpipe(55,"translate"),t.ɵɵtext(56,"help"),t.ɵɵelementEnd()(),t.ɵɵelementStart(57,"mat-form-field",20)(58,"mat-label",10),t.ɵɵtext(59,"tb.rulenode.request-timeout"),t.ɵɵelementEnd(),t.ɵɵelement(60,"input",23),t.ɵɵtemplate(61,cs,3,3,"mat-error",8)(62,fs,3,3,"mat-error",8),t.ɵɵelementStart(63,"mat-icon",22),t.ɵɵpipe(64,"translate"),t.ɵɵtext(65,"help"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(66,"div",24),t.ɵɵpipe(67,"translate"),t.ɵɵelementStart(68,"mat-slide-toggle",25),t.ɵɵtext(69),t.ɵɵpipe(70,"translate"),t.ɵɵelementEnd()()()()()()),2&e&&(t.ɵɵproperty("formGroup",n.lambdaConfigForm),t.ɵɵadvance(5),t.ɵɵproperty("hintText","tb.rulenode.template-rules-hint"),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(10,16,"tb.rulenode.function-name")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.lambdaConfigForm.get("functionName").hasError("required")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(16,18,"tb.rulenode.qualifier")),t.ɵɵadvance(15),t.ɵɵproperty("ngIf",n.lambdaConfigForm.get("accessKey").hasError("required")),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.lambdaConfigForm.get("secretKey").hasError("required")),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.lambdaConfigForm.get("region").hasError("required")),t.ɵɵadvance(12),t.ɵɵproperty("ngIf",n.lambdaConfigForm.get("connectionTimeout").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.lambdaConfigForm.get("connectionTimeout").hasError("min")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(55,20,"tb.rulenode.connection-timeout-hint")),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",n.lambdaConfigForm.get("requestTimeout").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.lambdaConfigForm.get("requestTimeout").hasError("min")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(64,22,"tb.rulenode.request-timeout-hint")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(67,24,"tb.rulenode.tell-failure-aws-lambda-hint")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(70,26,"tb.rulenode.tell-failure-aws-lambda")," "))},dependencies:t.ɵɵgetComponentDepsFactory(gs),encapsulation:2})}}e("LambdaConfigComponent",gs);class hs{static{this.ɵfac=function(e){return new(e||hs)}}static{this.ɵmod=t.ɵɵdefineNgModule({type:hs})}static{this.ɵinj=t.ɵɵdefineInjector({imports:[$,S,Q,xi,Wl,os,gs,Zo,_o,Jo,Yo,ll,Sl,Ll,Ao,jl,zl]})}}e("RulenodeCoreConfigExternalModule",hs),("undefined"==typeof ngJitMode||ngJitMode)&&t.ɵɵsetNgModuleScope(hs,{declarations:[Wl,os,gs,Zo,_o,Jo,Yo,ll,Sl,Ll,Ao,jl,zl],imports:[$,S,Q,xi],exports:[Wl,os,gs,Zo,_o,Jo,Yo,ll,Sl,Ll,Ao,jl,zl]});class ys extends i{constructor(e,t){super(e),this.store=e,this.fb=t,this.searchText=""}configForm(){return this.alarmStatusConfigForm}prepareInputConfig(e){return{alarmStatusList:P(e?.alarmStatusList)?e.alarmStatusList:null}}onConfigurationSet(e){this.alarmStatusConfigForm=this.fb.group({alarmStatusList:[e.alarmStatusList,[N.required]]})}static{this.ɵfac=function(e){return new(e||ys)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:ys,selectors:[["tb-filter-node-check-alarm-status-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:7,vars:2,consts:[[1,"tb-form-panel","stroked",3,"formGroup"],[1,"tb-form-row","no-padding","no-border","space-between"],["translate","",1,"tb-form-panel-title","tb-required"],["translate","",1,"tb-form-panel-hint","tb-error",3,"hidden"],["formControlName","alarmStatusList"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3,"tb.rulenode.alarm-status"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",3),t.ɵɵtext(5," tb.rulenode.alarm-required "),t.ɵɵelementEnd()(),t.ɵɵelement(6,"tb-alarm-status-select",4),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.alarmStatusConfigForm),t.ɵɵadvance(4),t.ɵɵproperty("hidden",n.alarmStatusConfigForm.get("alarmStatusList").valid))},dependencies:t.ɵɵgetComponentDepsFactory(ys),encapsulation:2})}}e("CheckAlarmStatusComponent",ys);const bs=e=>({inputName:e});class vs extends i{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.checkMessageConfigForm}prepareInputConfig(e){return{messageNames:P(e?.messageNames)?e.messageNames:[],metadataNames:P(e?.metadataNames)?e.metadataNames:[],checkAllKeys:!!P(e?.checkAllKeys)&&e.checkAllKeys}}prepareOutputConfig(e){return{messageNames:P(e?.messageNames)?e.messageNames:[],metadataNames:P(e?.metadataNames)?e.metadataNames:[],checkAllKeys:e.checkAllKeys}}atLeastOne(e,t=null){return n=>{t||(t=Object.keys(n.controls));return n?.controls&&t.some((t=>!e(n.controls[t])))?null:{atLeastOne:!0}}}onConfigurationSet(e){this.checkMessageConfigForm=this.fb.group({messageNames:[e.messageNames,[]],metadataNames:[e.metadataNames,[]],checkAllKeys:[e.checkAllKeys,[]]},{validators:this.atLeastOne(N.required,["messageNames","metadataNames"])})}get touchedValidationControl(){return["messageNames","metadataNames"].some((e=>this.checkMessageConfigForm.get(e).touched))}static{this.ɵfac=function(e){return new(e||vs)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:vs,selectors:[["tb-filter-node-check-message-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:25,vars:36,consts:[[1,"tb-form-panel","stroked",3,"formGroup"],[1,"tb-form-row","no-padding","no-border","space-between"],["translate","",1,"tb-form-panel-title","tb-required"],["translate","",1,"tb-form-panel-hint","tb-error",3,"hidden"],["editable","","subscriptSizing","dynamic","formControlName","messageNames",3,"label","placeholder"],["matSuffix","","color","primary","aria-hidden","false","aria-label","help-icon",1,"help-icon","margin-8","cursor-pointer",3,"matTooltip"],["editable","","subscriptSizing","dynamic","formControlName","metadataNames",3,"label","placeholder"],[1,"tb-form-row","no-border","no-padding",3,"tb-hint-tooltip-icon"],["formControlName","checkAllKeys",1,"mat-slide"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3,"tb.rulenode.fields-to-check"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",3),t.ɵɵtext(5," tb.rulenode.at-least-one-field-required "),t.ɵɵelementEnd()(),t.ɵɵelementStart(6,"tb-string-items-list",4),t.ɵɵpipe(7,"translate"),t.ɵɵpipe(8,"translate"),t.ɵɵelementStart(9,"mat-icon",5),t.ɵɵpipe(10,"translate"),t.ɵɵpipe(11,"translate"),t.ɵɵtext(12,"help"),t.ɵɵelementEnd()(),t.ɵɵelementStart(13,"tb-string-items-list",6),t.ɵɵpipe(14,"translate"),t.ɵɵpipe(15,"translate"),t.ɵɵelementStart(16,"mat-icon",5),t.ɵɵpipe(17,"translate"),t.ɵɵpipe(18,"translate"),t.ɵɵtext(19,"help"),t.ɵɵelementEnd()(),t.ɵɵelementStart(20,"div",7),t.ɵɵpipe(21,"translate"),t.ɵɵelementStart(22,"mat-slide-toggle",8),t.ɵɵtext(23),t.ɵɵpipe(24,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.checkMessageConfigForm),t.ɵɵadvance(4),t.ɵɵproperty("hidden",!(n.touchedValidationControl&&n.checkMessageConfigForm.hasError("atLeastOne"))),t.ɵɵadvance(2),t.ɵɵproperty("label",t.ɵɵpipeBind1(7,10,"tb.rulenode.data-keys"))("placeholder",t.ɵɵpipeBind1(8,12,"tb.rulenode.add-message-field")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind2(11,16,"tb.rulenode.chip-help",t.ɵɵpureFunction1(32,bs,t.ɵɵpipeBind1(10,14,"tb.rulenode.field-name")))),t.ɵɵadvance(4),t.ɵɵproperty("label",t.ɵɵpipeBind1(14,19,"tb.rulenode.metadata-keys"))("placeholder",t.ɵɵpipeBind1(15,21,"tb.rulenode.add-metadata-field")),t.ɵɵadvance(3),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind2(18,25,"tb.rulenode.chip-help",t.ɵɵpureFunction1(34,bs,t.ɵɵpipeBind1(17,23,"tb.rulenode.field-name")))),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(21,28,"tb.rulenode.check-all-keys-tooltip")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(24,30,"tb.rulenode.check-all-keys")," "))},dependencies:t.ɵɵgetComponentDepsFactory(vs),encapsulation:2})}}function xs(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",10),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementStart(3,"span",11),t.ɵɵtext(4,"tb.rulenode.relations-query-config-direction-suffix"),t.ɵɵelementEnd()()),2&e){const e=n.$implicit,r=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.entitySearchDirectionTranslationsMap.get(e))," ")}}function Cs(e,n){if(1&e&&t.ɵɵelement(0,"tb-entity-autocomplete",15),2&e){const e=t.ɵɵnextContext(2);t.ɵɵproperty("entityType",e.checkRelationConfigForm.get("entityType").value)}}function Ss(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",12),t.ɵɵelement(1,"tb-entity-type-select",13),t.ɵɵtemplate(2,Cs,1,1,"tb-entity-autocomplete",14),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.checkRelationConfigForm.get("entityType").value)}}e("CheckMessageConfigComponent",vs);class Ts extends i{constructor(e,t){super(e),this.store=e,this.fb=t,this.entitySearchDirection=Object.values(d),this.entitySearchDirectionTranslationsMap=b}configForm(){return this.checkRelationConfigForm}prepareInputConfig(e){return{checkForSingleEntity:!!P(e?.checkForSingleEntity)&&e.checkForSingleEntity,direction:P(e?.direction)?e.direction:null,entityType:P(e?.entityType)?e.entityType:null,entityId:P(e?.entityId)?e.entityId:null,relationType:P(e?.relationType)?e.relationType:null}}onConfigurationSet(e){this.checkRelationConfigForm=this.fb.group({checkForSingleEntity:[e.checkForSingleEntity,[]],direction:[e.direction,[]],entityType:[e.entityType,e&&e.checkForSingleEntity?[N.required]:[]],entityId:[e.entityId,e&&e.checkForSingleEntity?[N.required]:[]],relationType:[e.relationType,[N.required]]})}validatorTriggers(){return["checkForSingleEntity"]}updateValidators(e){const t=this.checkRelationConfigForm.get("checkForSingleEntity").value;this.checkRelationConfigForm.get("entityType").setValidators(t?[N.required]:[]),this.checkRelationConfigForm.get("entityType").updateValueAndValidity({emitEvent:e}),this.checkRelationConfigForm.get("entityId").setValidators(t?[N.required]:[]),this.checkRelationConfigForm.get("entityId").updateValueAndValidity({emitEvent:e})}static{this.ɵfac=function(e){return new(e||Ts)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Ts,selectors:[["tb-filter-node-check-relation-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:17,vars:12,consts:[[1,"tb-form-panel","stroked","no-padding-bottom",3,"formGroup"],["translate","",1,"tb-form-panel-title"],[1,"flex","flex-col"],["hideRequiredMarker","",1,"mat-block"],["formControlName","direction","required",""],[3,"value",4,"ngFor","ngForOf"],["required","","formControlName","relationType"],[1,"tb-form-row","no-border","no-padding","slide-toggle",3,"tb-hint-tooltip-icon"],["formControlName","checkForSingleEntity",1,"mat-slide"],["class","same-width-component-row",4,"ngIf"],[3,"value"],["translate",""],[1,"same-width-component-row"],["showLabel","","required","","formControlName","entityType",2,"min-width","100px","flex","1"],["class","flex-1","required","","formControlName","entityId",3,"entityType",4,"ngIf"],["required","","formControlName","entityId",1,"flex-1",3,"entityType"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1),t.ɵɵtext(2,"tb.rulenode.relation-search-parameters"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"div",2)(4,"mat-form-field",3)(5,"mat-label"),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(8,"mat-select",4),t.ɵɵtemplate(9,xs,5,4,"mat-option",5),t.ɵɵelementEnd()(),t.ɵɵelement(10,"tb-relation-type-autocomplete",6),t.ɵɵelementStart(11,"div",7),t.ɵɵpipe(12,"translate"),t.ɵɵelementStart(13,"mat-slide-toggle",8),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd()(),t.ɵɵtemplate(16,Ss,3,1,"div",9),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.checkRelationConfigForm),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(7,6,"relation.direction")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.entitySearchDirection),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(12,8,"tb.rulenode.check-relation-to-specific-entity-tooltip")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(15,10,"tb.rulenode.check-relation-to-specific-entity")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.checkRelationConfigForm.get("checkForSingleEntity").value))},dependencies:t.ɵɵgetComponentDepsFactory(Ts),styles:["[_nghost-%COMP%] .slide-toggle[_ngcontent-%COMP%]{margin-bottom:18px}"]})}}e("CheckRelationConfigComponent",Ts);const Is=e=>({perimeterKeyName:e});function Es(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.latitude-field-name-required")," "))}function Fs(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.longitude-field-name-required")," "))}function qs(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",18),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.perimeterTypeTranslationMap.get(e))," ")}}function As(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.perimeter-key-name-required")," "))}function ks(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",19)(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",20),t.ɵɵtemplate(5,As,3,3,"mat-error",6),t.ɵɵelementStart(6,"mat-hint"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,3,"tb.rulenode.perimeter-key-name")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",e.geoFilterConfigForm.get("perimeterKeyName").hasError("required")),t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(8,5,"tb.rulenode.perimeter-key-name-hint"))}}function Ns(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.circle-center-latitude-required")," "))}function ws(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.circle-center-longitude-required")," "))}function Ms(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.range-required")," "))}function Bs(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",18),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext(2);t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.rangeUnitTranslationMap.get(e))," ")}}function Vs(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.range-units-required")," "))}function Os(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",9)(1,"div",3)(2,"mat-form-field",21)(3,"mat-label"),t.ɵɵtext(4),t.ɵɵpipe(5,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(6,"input",22),t.ɵɵtemplate(7,Ns,3,3,"mat-error",6),t.ɵɵelementEnd(),t.ɵɵelementStart(8,"mat-form-field",21)(9,"mat-label"),t.ɵɵtext(10),t.ɵɵpipe(11,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(12,"input",23),t.ɵɵtemplate(13,ws,3,3,"mat-error",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(14,"div",3)(15,"mat-form-field",21)(16,"mat-label"),t.ɵɵtext(17),t.ɵɵpipe(18,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(19,"input",24),t.ɵɵtemplate(20,Ms,3,3,"mat-error",6),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"mat-form-field",21)(22,"mat-label"),t.ɵɵtext(23),t.ɵɵpipe(24,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(25,"mat-select",25),t.ɵɵtemplate(26,Bs,3,4,"mat-option",12),t.ɵɵelementEnd(),t.ɵɵtemplate(27,Vs,3,3,"mat-error",6),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(5,9,"tb.rulenode.circle-center-latitude")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",e.geoFilterConfigForm.get("centerLatitude").hasError("required")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(11,11,"tb.rulenode.circle-center-longitude")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",e.geoFilterConfigForm.get("centerLongitude").hasError("required")),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(18,13,"tb.rulenode.range")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",e.geoFilterConfigForm.get("range").hasError("required")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(24,15,"tb.rulenode.range-units")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",e.rangeUnits),t.ɵɵadvance(),t.ɵɵproperty("ngIf",e.geoFilterConfigForm.get("rangeUnit").hasError("required"))}}function Ds(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.polygon-definition-required")," "))}function Ls(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-form-field",26)(1,"mat-label"),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"input",27),t.ɵɵelementStart(5,"mat-hint"),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(8,Ds,3,3,"mat-error",6),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(2),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(3,3,"tb.rulenode.polygon-definition")),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(7,5,"tb.rulenode.polygon-definition-hint")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",e.geoFilterConfigForm.get("polygonsDefinition").hasError("required"))}}class Ps extends i{constructor(e,t){super(e),this.store=e,this.fb=t,this.perimeterType=ft,this.perimeterTypes=Object.values(ft),this.perimeterTypeTranslationMap=gt,this.rangeUnits=Object.values(bt),this.rangeUnitTranslationMap=vt,this.defaultPaddingEnable=!0}configForm(){return this.geoFilterConfigForm}prepareInputConfig(e){return{latitudeKeyName:P(e?.latitudeKeyName)?e.latitudeKeyName:null,longitudeKeyName:P(e?.longitudeKeyName)?e.longitudeKeyName:null,perimeterType:P(e?.perimeterType)?e.perimeterType:null,fetchPerimeterInfoFromMessageMetadata:!!P(e?.fetchPerimeterInfoFromMessageMetadata)&&e.fetchPerimeterInfoFromMessageMetadata,perimeterKeyName:P(e?.perimeterKeyName)?e.perimeterKeyName:null,centerLatitude:P(e?.centerLatitude)?e.centerLatitude:null,centerLongitude:P(e?.centerLongitude)?e.centerLongitude:null,range:P(e?.range)?e.range:null,rangeUnit:P(e?.rangeUnit)?e.rangeUnit:null,polygonsDefinition:P(e?.polygonsDefinition)?e.polygonsDefinition:null}}onConfigurationSet(e){this.geoFilterConfigForm=this.fb.group({latitudeKeyName:[e.latitudeKeyName,[N.required]],longitudeKeyName:[e.longitudeKeyName,[N.required]],perimeterType:[e.perimeterType,[N.required]],fetchPerimeterInfoFromMessageMetadata:[e.fetchPerimeterInfoFromMessageMetadata,[]],perimeterKeyName:[e.perimeterKeyName,[]],centerLatitude:[e.centerLatitude,[]],centerLongitude:[e.centerLongitude,[]],range:[e.range,[]],rangeUnit:[e.rangeUnit,[]],polygonsDefinition:[e.polygonsDefinition,[]]})}validatorTriggers(){return["fetchPerimeterInfoFromMessageMetadata","perimeterType"]}updateValidators(e){const t=this.geoFilterConfigForm.get("fetchPerimeterInfoFromMessageMetadata").value,n=this.geoFilterConfigForm.get("perimeterType").value;t?this.geoFilterConfigForm.get("perimeterKeyName").setValidators([N.required]):this.geoFilterConfigForm.get("perimeterKeyName").setValidators([]),t||n!==ft.CIRCLE?(this.geoFilterConfigForm.get("centerLatitude").setValidators([]),this.geoFilterConfigForm.get("centerLongitude").setValidators([]),this.geoFilterConfigForm.get("range").setValidators([]),this.geoFilterConfigForm.get("rangeUnit").setValidators([]),this.defaultPaddingEnable=!0):(this.geoFilterConfigForm.get("centerLatitude").setValidators([N.required,N.min(-90),N.max(90)]),this.geoFilterConfigForm.get("centerLongitude").setValidators([N.required,N.min(-180),N.max(180)]),this.geoFilterConfigForm.get("range").setValidators([N.required,N.min(0)]),this.geoFilterConfigForm.get("rangeUnit").setValidators([N.required]),this.defaultPaddingEnable=!1),t||n!==ft.POLYGON?this.geoFilterConfigForm.get("polygonsDefinition").setValidators([]):this.geoFilterConfigForm.get("polygonsDefinition").setValidators([N.required]),this.geoFilterConfigForm.get("perimeterKeyName").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("centerLatitude").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("centerLongitude").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("range").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("rangeUnit").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("polygonsDefinition").updateValueAndValidity({emitEvent:e})}static{this.ɵfac=function(e){return new(e||Ps)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Ps,selectors:[["tb-filter-node-gps-geofencing-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:39,vars:32,consts:[[1,"tb-form-panel","no-border","no-padding",3,"formGroup"],[1,"tb-form-panel","stroked"],["translate","",1,"tb-form-panel-title"],[1,"flex","flex-row","gap-4"],[1,"mat-block","max-w-50%","flex-full"],["matInput","","formControlName","latitudeKeyName","required",""],[4,"ngIf"],["matInput","","formControlName","longitudeKeyName","required",""],["translate","",1,"tb-form-hint","tb-primary-fill"],[1,"flex","flex-col"],["hideRequiredMarker","",1,"mat-block","flex-1"],["formControlName","perimeterType"],[3,"value",4,"ngFor","ngForOf"],[1,"tb-form-row","no-border","no-padding","slide-toggle",3,"tb-hint-tooltip-icon"],["formControlName","fetchPerimeterInfoFromMessageMetadata",1,"mat-slide"],["class","mat-block",4,"ngIf"],["class","flex flex-col",4,"ngIf"],["class","mat-block","subscriptSizing","dynamic",4,"ngIf"],[3,"value"],[1,"mat-block"],["matInput","","formControlName","perimeterKeyName","required",""],[1,"flex-1"],["type","number","min","-90","max","90","step","0.1","matInput","","formControlName","centerLatitude","required",""],["type","number","min","-180","max","180","step","0.1","matInput","","formControlName","centerLongitude","required",""],["type","number","min","0","step","0.1","matInput","","formControlName","range","required",""],["formControlName","rangeUnit","required",""],["subscriptSizing","dynamic",1,"mat-block"],["matInput","","formControlName","polygonsDefinition","required",""]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"section",1)(2,"div",2),t.ɵɵtext(3,"tb.rulenode.coordinate-field-names"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"section")(5,"div",3)(6,"mat-form-field",4)(7,"mat-label"),t.ɵɵtext(8),t.ɵɵpipe(9,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(10,"input",5),t.ɵɵtemplate(11,Es,3,3,"mat-error",6),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"mat-form-field",4)(13,"mat-label"),t.ɵɵtext(14),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(16,"input",7),t.ɵɵtemplate(17,Fs,3,3,"mat-error",6),t.ɵɵelementEnd()(),t.ɵɵelementStart(18,"div",8),t.ɵɵtext(19,"tb.rulenode.coordinate-field-hint"),t.ɵɵelementEnd()()(),t.ɵɵelementStart(20,"section",1)(21,"div",2),t.ɵɵtext(22,"tb.rulenode.geofence-configuration"),t.ɵɵelementEnd(),t.ɵɵelementStart(23,"section",9)(24,"mat-form-field",10)(25,"mat-label"),t.ɵɵtext(26),t.ɵɵpipe(27,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(28,"mat-select",11),t.ɵɵtemplate(29,qs,3,4,"mat-option",12),t.ɵɵelementEnd()(),t.ɵɵelementStart(30,"div",13),t.ɵɵpipe(31,"translate"),t.ɵɵpipe(32,"translate"),t.ɵɵelementStart(33,"mat-slide-toggle",14),t.ɵɵtext(34),t.ɵɵpipe(35,"translate"),t.ɵɵelementEnd()(),t.ɵɵtemplate(36,ks,9,7,"mat-form-field",15)(37,Os,28,17,"div",16)(38,Ls,9,7,"mat-form-field",17),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.geoFilterConfigForm),t.ɵɵadvance(8),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(9,14,"tb.rulenode.latitude-field-name")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.geoFilterConfigForm.get("latitudeKeyName").hasError("required")),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(15,16,"tb.rulenode.longitude-field-name")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.geoFilterConfigForm.get("longitudeKeyName").hasError("required")),t.ɵɵadvance(3),t.ɵɵclassProp("no-padding-bottom",!n.defaultPaddingEnable),t.ɵɵadvance(6),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(27,18,"tb.rulenode.perimeter-type")),t.ɵɵadvance(3),t.ɵɵproperty("ngForOf",n.perimeterTypes),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",n.geoFilterConfigForm.get("perimeterType").value===n.perimeterType.CIRCLE?t.ɵɵpipeBind2(31,20,"tb.rulenode.fetch-circle-parameter-info-from-metadata-hint",t.ɵɵpureFunction1(28,Is,n.geoFilterConfigForm.get("perimeterKeyName").valid?n.geoFilterConfigForm.get("perimeterKeyName").value:"ss_perimeter")):t.ɵɵpipeBind2(32,23,"tb.rulenode.fetch-poligon-parameter-info-from-metadata-hint",t.ɵɵpureFunction1(30,Is,n.geoFilterConfigForm.get("perimeterKeyName").valid?n.geoFilterConfigForm.get("perimeterKeyName").value:"ss_perimeter"))),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(35,26,"tb.rulenode.fetch-perimeter-info-from-metadata")," "),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.geoFilterConfigForm.get("fetchPerimeterInfoFromMessageMetadata").value),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.geoFilterConfigForm.get("perimeterType").value===n.perimeterType.CIRCLE&&!n.geoFilterConfigForm.get("fetchPerimeterInfoFromMessageMetadata").value),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.geoFilterConfigForm.get("perimeterType").value===n.perimeterType.POLYGON&&!n.geoFilterConfigForm.get("fetchPerimeterInfoFromMessageMetadata").value))},dependencies:t.ɵɵgetComponentDepsFactory(Ps),styles:["[_nghost-%COMP%] .slide-toggle[_ngcontent-%COMP%]{margin-bottom:18px}"]})}}e("GpsGeoFilterConfigComponent",Ps);class Rs extends i{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.messageTypeConfigForm}prepareInputConfig(e){return{messageTypes:P(e?.messageTypes)?e.messageTypes:null}}onConfigurationSet(e){this.messageTypeConfigForm=this.fb.group({messageTypes:[e.messageTypes,[N.required]]})}static{this.ɵfac=function(e){return new(e||Rs)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Rs,selectors:[["tb-filter-node-message-type-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:3,vars:4,consts:[[3,"formGroup"],["required","","formControlName","messageTypes",3,"label"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0),t.ɵɵelement(1,"tb-message-types-config",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.messageTypeConfigForm),t.ɵɵadvance(),t.ɵɵproperty("label",t.ɵɵpipeBind1(2,2,"tb.rulenode.select-message-types")))},dependencies:t.ɵɵgetComponentDepsFactory(Rs),encapsulation:2})}}e("MessageTypeConfigComponent",Rs);const _s=e=>({inputName:e});class js extends i{constructor(e,t){super(e),this.store=e,this.fb=t,this.allowedEntityTypes=[u.DEVICE,u.ASSET,u.ENTITY_VIEW,u.TENANT,u.CUSTOMER,u.USER,u.DASHBOARD,u.RULE_CHAIN,u.RULE_NODE,u.EDGE]}configForm(){return this.originatorTypeConfigForm}prepareInputConfig(e){return{originatorTypes:P(e?.originatorTypes)?e.originatorTypes:null}}onConfigurationSet(e){this.originatorTypeConfigForm=this.fb.group({originatorTypes:[e.originatorTypes,[N.required]]})}static{this.ɵfac=function(e){return new(e||js)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:js,selectors:[["tb-filter-node-originator-type-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:9,vars:20,consts:[[3,"formGroup"],["formControlName","originatorTypes","required","",1,"flex-1",3,"allowedEntityTypes","ignoreAuthorityFilter","emptyInputPlaceholder","filledInputPlaceholder","label"],["matSuffix","","aria-hidden","false","aria-label","help-icon","color","primary",1,"help-icon","margin-8","cursor-pointer",3,"matTooltip"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"tb-entity-type-list",1),t.ɵɵpipe(2,"translate"),t.ɵɵpipe(3,"translate"),t.ɵɵpipe(4,"translate"),t.ɵɵelementStart(5,"mat-icon",2),t.ɵɵpipe(6,"translate"),t.ɵɵpipe(7,"translate"),t.ɵɵtext(8,"help"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.originatorTypeConfigForm),t.ɵɵadvance(),t.ɵɵproperty("allowedEntityTypes",n.allowedEntityTypes)("ignoreAuthorityFilter",!0)("emptyInputPlaceholder",t.ɵɵpipeBind1(2,7,"tb.rulenode.add-entity-type"))("filledInputPlaceholder",t.ɵɵpipeBind1(3,9,"tb.rulenode.add-entity-type"))("label",t.ɵɵpipeBind1(4,11,"tb.rulenode.select-entity-types")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind2(7,15,"tb.rulenode.chip-help",t.ɵɵpureFunction1(18,_s,t.ɵɵpipeBind1(6,13,"tb.rulenode.entity-type")))))},dependencies:t.ɵɵgetComponentDepsFactory(js),encapsulation:2})}}e("OriginatorTypeConfigComponent",js);const Gs=["jsFuncComponent"],Ks=["tbelFuncComponent"],Us=()=>["msg","metadata","msgType"];function Hs(e,n){1&e&&t.ɵɵelement(0,"tb-script-lang",7)}function zs(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-js-func",8,0)(2,"button",9),t.ɵɵpipe(3,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.testScript())})),t.ɵɵelementStart(4,"mat-icon",10),t.ɵɵtext(5,"bug_report"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("functionArgs",t.ɵɵpureFunction0(4,Us)),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(3,2,e.testScriptLabel))}}function $s(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-js-func",11,1)(2,"button",9),t.ɵɵpipe(3,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.testScript())})),t.ɵɵelementStart(4,"mat-icon",10),t.ɵɵtext(5,"bug_report"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("functionArgs",t.ɵɵpureFunction0(6,Us))("disableUndefinedCheck",!0)("scriptLanguage",e.scriptLanguage.TBEL),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(3,4,e.testScriptLabel))}}class Qs extends i{constructor(e,t,r,a){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=r,this.translate=a,this.tbelEnabled=D(this.store).tbelEnabled,this.scriptLanguage=s,this.changeScript=new n,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-filter-function"}configForm(){return this.scriptConfigForm}onConfigurationSet(e){this.scriptConfigForm=this.fb.group({scriptLang:[e.scriptLang,[N.required]],jsScript:[e.jsScript,[]],tbelScript:[e.tbelScript,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.scriptConfigForm.get("scriptLang").value;t!==s.TBEL||this.tbelEnabled||(t=s.JS,this.scriptConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.scriptConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.scriptConfigForm.get("jsScript").setValidators(t===s.JS?[N.required]:[]),this.scriptConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.scriptConfigForm.get("tbelScript").setValidators(t===s.TBEL?[N.required]:[]),this.scriptConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=s.JS)),{scriptLang:P(e?.scriptLang)?e.scriptLang:s.JS,jsScript:P(e?.jsScript)?e.jsScript:null,tbelScript:P(e?.tbelScript)?e.tbelScript:null}}testScript(e){const t=this.scriptConfigForm.get("scriptLang").value,n=t===s.JS?"jsScript":"tbelScript",r=t===s.JS?"rulenode/filter_node_script_fn":"rulenode/tbel/filter_node_script_fn",a=this.scriptConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(a,"filter",this.translate.instant("tb.rulenode.filter"),"Filter",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.scriptConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.scriptConfigForm.get("scriptLang").value===s.JS&&this.jsFuncComponent.validateOnSubmit()}static{this.ɵfac=function(e){return new(e||Qs)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder),t.ɵɵdirectiveInject(L.NodeScriptTestService),t.ɵɵdirectiveInject(K.TranslateService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Qs,selectors:[["tb-filter-node-script-config"]],viewQuery:function(e,n){if(1&e&&(t.ɵɵviewQuery(Gs,5),t.ɵɵviewQuery(Ks,5)),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.jsFuncComponent=e.first),t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.tbelFuncComponent=e.first)}},features:[t.ɵɵInheritDefinitionFeature],decls:7,vars:7,consts:[["jsFuncComponent",""],["tbelFuncComponent",""],[3,"formGroup"],["formControlName","scriptLang",4,"ngIf"],["formControlName","jsScript","functionName","Filter","helpId","rulenode/filter_node_script_fn","noValidate","true",3,"functionArgs",4,"ngIf"],["formControlName","tbelScript","functionName","Filter","helpId","rulenode/tbel/filter_node_script_fn","noValidate","true",3,"functionArgs","disableUndefinedCheck","scriptLanguage",4,"ngIf"],["mat-button","","mat-raised-button","","color","primary",3,"click"],["formControlName","scriptLang"],["formControlName","jsScript","functionName","Filter","helpId","rulenode/filter_node_script_fn","noValidate","true",3,"functionArgs"],["toolbarSuffixButton","","mat-icon-button","","matTooltipPosition","above",1,"tb-mat-32",3,"click","matTooltip"],["color","primary",1,"material-icons"],["formControlName","tbelScript","functionName","Filter","helpId","rulenode/tbel/filter_node_script_fn","noValidate","true",3,"functionArgs","disableUndefinedCheck","scriptLanguage"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",2),t.ɵɵtemplate(1,Hs,1,0,"tb-script-lang",3)(2,zs,6,5,"tb-js-func",4)(3,$s,6,7,"tb-js-func",5),t.ɵɵelementStart(4,"button",6),t.ɵɵlistener("click",(function(){return n.testScript()})),t.ɵɵtext(5),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.scriptConfigForm),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.tbelEnabled),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.scriptConfigForm.get("scriptLang").value===n.scriptLanguage.JS),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.scriptConfigForm.get("scriptLang").value===n.scriptLanguage.TBEL),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(6,5,n.testScriptLabel)," "))},dependencies:t.ɵɵgetComponentDepsFactory(Qs),encapsulation:2})}}e("ScriptConfigComponent",Qs);const Js=["jsFuncComponent"],Ys=["tbelFuncComponent"],Ws=()=>["msg","metadata","msgType"];function Xs(e,n){1&e&&t.ɵɵelement(0,"tb-script-lang",7)}function Zs(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-js-func",8,0)(2,"button",9),t.ɵɵpipe(3,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.testScript())})),t.ɵɵelementStart(4,"mat-icon",10),t.ɵɵtext(5,"bug_report"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("functionArgs",t.ɵɵpureFunction0(4,Ws)),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(3,2,e.testScriptLabel))}}function ep(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-js-func",11,1)(2,"button",9),t.ɵɵpipe(3,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.testScript())})),t.ɵɵelementStart(4,"mat-icon",10),t.ɵɵtext(5,"bug_report"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("functionArgs",t.ɵɵpureFunction0(6,Ws))("disableUndefinedCheck",!0)("scriptLanguage",e.scriptLanguage.TBEL),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(3,4,e.testScriptLabel))}}class tp extends i{constructor(e,t,r,a){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=r,this.translate=a,this.tbelEnabled=D(this.store).tbelEnabled,this.scriptLanguage=s,this.changeScript=new n,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-switch-function"}configForm(){return this.switchConfigForm}onConfigurationSet(e){this.switchConfigForm=this.fb.group({scriptLang:[e.scriptLang,[N.required]],jsScript:[e.jsScript,[]],tbelScript:[e.tbelScript,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.switchConfigForm.get("scriptLang").value;t!==s.TBEL||this.tbelEnabled||(t=s.JS,this.switchConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.switchConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.switchConfigForm.get("jsScript").setValidators(t===s.JS?[N.required]:[]),this.switchConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.switchConfigForm.get("tbelScript").setValidators(t===s.TBEL?[N.required]:[]),this.switchConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=s.JS)),{scriptLang:P(e?.scriptLang)?e.scriptLang:s.JS,jsScript:P(e?.jsScript)?e.jsScript:null,tbelScript:P(e?.tbelScript)?e.tbelScript:null}}testScript(e){const t=this.switchConfigForm.get("scriptLang").value,n=t===s.JS?"jsScript":"tbelScript",r=t===s.JS?"rulenode/switch_node_script_fn":"rulenode/tbel/switch_node_script_fn",a=this.switchConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(a,"switch",this.translate.instant("tb.rulenode.switch"),"Switch",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.switchConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.switchConfigForm.get("scriptLang").value===s.JS&&this.jsFuncComponent.validateOnSubmit()}static{this.ɵfac=function(e){return new(e||tp)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder),t.ɵɵdirectiveInject(L.NodeScriptTestService),t.ɵɵdirectiveInject(K.TranslateService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:tp,selectors:[["tb-filter-node-switch-config"]],viewQuery:function(e,n){if(1&e&&(t.ɵɵviewQuery(Js,5),t.ɵɵviewQuery(Ys,5)),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.jsFuncComponent=e.first),t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.tbelFuncComponent=e.first)}},features:[t.ɵɵInheritDefinitionFeature],decls:7,vars:7,consts:[["jsFuncComponent",""],["tbelFuncComponent",""],[3,"formGroup"],["formControlName","scriptLang",4,"ngIf"],["formControlName","jsScript","functionName","Switch","helpId","rulenode/switch_node_script_fn","noValidate","true",3,"functionArgs",4,"ngIf"],["formControlName","tbelScript","functionName","Switch","helpId","rulenode/tbel/switch_node_script_fn","noValidate","true",3,"functionArgs","disableUndefinedCheck","scriptLanguage",4,"ngIf"],["mat-button","","mat-raised-button","","color","primary",3,"click"],["formControlName","scriptLang"],["formControlName","jsScript","functionName","Switch","helpId","rulenode/switch_node_script_fn","noValidate","true",3,"functionArgs"],["toolbarSuffixButton","","mat-icon-button","","matTooltipPosition","above",1,"tb-mat-32",3,"click","matTooltip"],["color","primary",1,"material-icons"],["formControlName","tbelScript","functionName","Switch","helpId","rulenode/tbel/switch_node_script_fn","noValidate","true",3,"functionArgs","disableUndefinedCheck","scriptLanguage"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",2),t.ɵɵtemplate(1,Xs,1,0,"tb-script-lang",3)(2,Zs,6,5,"tb-js-func",4)(3,ep,6,7,"tb-js-func",5),t.ɵɵelementStart(4,"button",6),t.ɵɵlistener("click",(function(){return n.testScript()})),t.ɵɵtext(5),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.switchConfigForm),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.tbelEnabled),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.switchConfigForm.get("scriptLang").value===n.scriptLanguage.JS),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.switchConfigForm.get("scriptLang").value===n.scriptLanguage.TBEL),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(6,5,n.testScriptLabel)," "))},dependencies:t.ɵɵgetComponentDepsFactory(tp),encapsulation:2})}}e("SwitchConfigComponent",tp);class np{static{this.ɵfac=function(e){return new(e||np)}}static{this.ɵmod=t.ɵɵdefineNgModule({type:np})}static{this.ɵinj=t.ɵɵdefineInjector({imports:[$,S,xi,vs,Ts,Ps,Rs,js,Qs,tp,ys]})}}function rp(e,n){if(1&e&&(t.ɵɵelementStart(0,"span"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,e.originatorSourceTranslationMap.get(e.changeOriginatorConfigForm.get("originatorSource").value))," ")}}function ap(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",8)(1,"span",9),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"br"),t.ɵɵelementStart(5,"small",10),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd()()),2&e){const e=n.$implicit,r=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,3,r.originatorSourceTranslationMap.get(e))," "),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(7,5,r.originatorSourceDescTranslationMap.get(e))," ")}}function ip(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.entity-name-pattern-required")," "))}function op(e,n){if(1&e&&(t.ɵɵelementStart(0,"div",11),t.ɵɵelement(1,"tb-example-hint",12),t.ɵɵelementStart(2,"div",13),t.ɵɵelement(3,"tb-entity-type-select",14),t.ɵɵelementStart(4,"mat-form-field",15)(5,"mat-label",2),t.ɵɵtext(6,"tb.rulenode.entity-name-pattern"),t.ɵɵelementEnd(),t.ɵɵelement(7,"input",16),t.ɵɵtemplate(8,ip,3,3,"mat-error",4),t.ɵɵelementEnd()()()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵproperty("hintText","tb.rulenode.entity-name-pattern-hint"),t.ɵɵadvance(2),t.ɵɵproperty("allowedEntityTypes",e.allowedEntityTypes),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",e.changeOriginatorConfigForm.get("entityNamePattern").hasError("required")||e.changeOriginatorConfigForm.get("entityNamePattern").hasError("pattern"))}}function lp(e,n){1&e&&t.ɵɵelement(0,"tb-relations-query-config",17)}e("RuleNodeCoreConfigFilterModule",np),("undefined"==typeof ngJitMode||ngJitMode)&&t.ɵɵsetNgModuleScope(np,{declarations:[vs,Ts,Ps,Rs,js,Qs,tp,ys],imports:[$,S,xi],exports:[vs,Ts,Ps,Rs,js,Qs,tp,ys]});class sp extends i{constructor(e,t){super(e),this.store=e,this.fb=t,this.originatorSource=pt,this.originatorSources=Object.keys(pt),this.originatorSourceTranslationMap=mt,this.originatorSourceDescTranslationMap=dt,this.allowedEntityTypes=[u.DEVICE,u.ASSET,u.ENTITY_VIEW,u.USER,u.EDGE]}configForm(){return this.changeOriginatorConfigForm}onConfigurationSet(e){this.changeOriginatorConfigForm=this.fb.group({originatorSource:[e?e.originatorSource:null,[N.required]],entityType:[e?e.entityType:null,[]],entityNamePattern:[e?e.entityNamePattern:null,[]],relationsQuery:[e?e.relationsQuery:null,[]]})}validatorTriggers(){return["originatorSource"]}updateValidators(e){const t=this.changeOriginatorConfigForm.get("originatorSource").value;t===pt.RELATED?this.changeOriginatorConfigForm.get("relationsQuery").setValidators([N.required]):this.changeOriginatorConfigForm.get("relationsQuery").setValidators([]),t===pt.ENTITY?(this.changeOriginatorConfigForm.get("entityType").setValidators([N.required]),this.changeOriginatorConfigForm.get("entityNamePattern").setValidators([N.required,N.pattern(/.*\S.*/)])):(this.changeOriginatorConfigForm.get("entityType").patchValue(null,{emitEvent:e}),this.changeOriginatorConfigForm.get("entityNamePattern").patchValue(null,{emitEvent:e}),this.changeOriginatorConfigForm.get("entityType").setValidators([]),this.changeOriginatorConfigForm.get("entityNamePattern").setValidators([])),this.changeOriginatorConfigForm.get("relationsQuery").updateValueAndValidity({emitEvent:e}),this.changeOriginatorConfigForm.get("entityType").updateValueAndValidity({emitEvent:e}),this.changeOriginatorConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e})}static{this.ɵfac=function(e){return new(e||sp)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:sp,selectors:[["tb-transformation-node-change-originator-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:10,vars:5,consts:[[1,"tb-form-panel","no-border","no-padding",3,"formGroup"],["subscriptSizing","dynamic","hideRequiredMarker","",1,"mat-block"],["translate",""],["formControlName","originatorSource","required",""],[4,"ngIf"],[3,"value",4,"ngFor","ngForOf"],["class","tb-form-panel stroked no-padding-bottom",4,"ngIf"],["required","","formControlName","relationsQuery",4,"ngIf"],[3,"value"],["matListItemTitle",""],["matListItemMeta","",2,"color","inherit"],[1,"tb-form-panel","stroked","no-padding-bottom"],["popupHelpLink","rulenode/change_originator_node_fields_templatization",3,"hintText"],[1,"tb-form-row","no-border","no-padding","tb-standard-fields"],["showLabel","","required","","formControlName","entityType",1,"mat-mdc-form-field","flex",3,"allowedEntityTypes"],[1,"flex"],["required","","matInput","","formControlName","entityNamePattern"],["required","","formControlName","relationsQuery"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"mat-form-field",1)(2,"mat-label",2),t.ɵɵtext(3,"tb.rulenode.new-originator"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"mat-select",3)(5,"mat-select-trigger"),t.ɵɵtemplate(6,rp,3,3,"span",4),t.ɵɵelementEnd(),t.ɵɵtemplate(7,ap,8,7,"mat-option",5),t.ɵɵelementEnd()(),t.ɵɵtemplate(8,op,9,3,"div",6)(9,lp,1,0,"tb-relations-query-config",7),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.changeOriginatorConfigForm),t.ɵɵadvance(6),t.ɵɵproperty("ngIf",n.originatorSourceTranslationMap.get(n.changeOriginatorConfigForm.get("originatorSource").value)),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",n.originatorSources),t.ɵɵadvance(),t.ɵɵproperty("ngIf","ENTITY"===n.changeOriginatorConfigForm.get("originatorSource").value),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.changeOriginatorConfigForm.get("originatorSource").value===n.originatorSource.RELATED))},dependencies:t.ɵɵgetComponentDepsFactory(sp),encapsulation:2})}}e("ChangeOriginatorConfigComponent",sp);const pp=["jsFuncComponent"],mp=["tbelFuncComponent"],dp=()=>["msg","metadata","msgType"];function up(e,n){1&e&&t.ɵɵelement(0,"tb-script-lang",7)}function cp(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-js-func",8,0)(2,"button",9),t.ɵɵpipe(3,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.testScript())})),t.ɵɵelementStart(4,"mat-icon",10),t.ɵɵtext(5,"bug_report"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("functionArgs",t.ɵɵpureFunction0(4,dp)),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(3,2,e.testScriptLabel))}}function fp(e,n){if(1&e){const e=t.ɵɵgetCurrentView();t.ɵɵelementStart(0,"tb-js-func",11,1)(2,"button",9),t.ɵɵpipe(3,"translate"),t.ɵɵlistener("click",(function(){t.ɵɵrestoreView(e);const n=t.ɵɵnextContext();return t.ɵɵresetView(n.testScript())})),t.ɵɵelementStart(4,"mat-icon",10),t.ɵɵtext(5,"bug_report"),t.ɵɵelementEnd()()()}if(2&e){const e=t.ɵɵnextContext();t.ɵɵproperty("functionArgs",t.ɵɵpureFunction0(6,dp))("disableUndefinedCheck",!0)("scriptLanguage",e.scriptLanguage.TBEL),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(3,4,e.testScriptLabel))}}class gp extends i{constructor(e,t,r,a){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=r,this.translate=a,this.tbelEnabled=D(this.store).tbelEnabled,this.scriptLanguage=s,this.changeScript=new n,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-transformer-function"}configForm(){return this.scriptConfigForm}onConfigurationSet(e){this.scriptConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:s.JS,[N.required]],jsScript:[e?e.jsScript:null,[N.required]],tbelScript:[e?e.tbelScript:null,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.scriptConfigForm.get("scriptLang").value;t!==s.TBEL||this.tbelEnabled||(t=s.JS,this.scriptConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.scriptConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.scriptConfigForm.get("jsScript").setValidators(t===s.JS?[N.required]:[]),this.scriptConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.scriptConfigForm.get("tbelScript").setValidators(t===s.TBEL?[N.required]:[]),this.scriptConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=s.JS)),e}testScript(e){const t=this.scriptConfigForm.get("scriptLang").value,n=t===s.JS?"jsScript":"tbelScript",r=t===s.JS?"rulenode/transformation_node_script_fn":"rulenode/tbel/transformation_node_script_fn",a=this.scriptConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(a,"update",this.translate.instant("tb.rulenode.transformer"),"Transform",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.scriptConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.scriptConfigForm.get("scriptLang").value===s.JS&&this.jsFuncComponent.validateOnSubmit()}static{this.ɵfac=function(e){return new(e||gp)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.FormBuilder),t.ɵɵdirectiveInject(L.NodeScriptTestService),t.ɵɵdirectiveInject(K.TranslateService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:gp,selectors:[["tb-transformation-node-script-config"]],viewQuery:function(e,n){if(1&e&&(t.ɵɵviewQuery(pp,5),t.ɵɵviewQuery(mp,5)),2&e){let e;t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.jsFuncComponent=e.first),t.ɵɵqueryRefresh(e=t.ɵɵloadQuery())&&(n.tbelFuncComponent=e.first)}},features:[t.ɵɵInheritDefinitionFeature],decls:8,vars:7,consts:[["jsFuncComponent",""],["tbelFuncComponent",""],[3,"formGroup"],["formControlName","scriptLang",4,"ngIf"],["formControlName","jsScript","functionName","Transform","helpId","rulenode/transformation_node_script_fn","noValidate","true",3,"functionArgs",4,"ngIf"],["formControlName","tbelScript","functionName","Transform","helpId","rulenode/tbel/transformation_node_script_fn","noValidate","true",3,"functionArgs","disableUndefinedCheck","scriptLanguage",4,"ngIf"],["mat-button","","mat-raised-button","","color","primary",3,"click"],["formControlName","scriptLang"],["formControlName","jsScript","functionName","Transform","helpId","rulenode/transformation_node_script_fn","noValidate","true",3,"functionArgs"],["toolbarSuffixButton","","mat-icon-button","","matTooltipPosition","above",1,"tb-mat-32",3,"click","matTooltip"],["color","primary",1,"material-icons"],["formControlName","tbelScript","functionName","Transform","helpId","rulenode/tbel/transformation_node_script_fn","noValidate","true",3,"functionArgs","disableUndefinedCheck","scriptLanguage"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",2),t.ɵɵtemplate(1,up,1,0,"tb-script-lang",3)(2,cp,6,5,"tb-js-func",4)(3,fp,6,7,"tb-js-func",5),t.ɵɵelementStart(4,"div")(5,"button",6),t.ɵɵlistener("click",(function(){return n.testScript()})),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.scriptConfigForm),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.tbelEnabled),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.scriptConfigForm.get("scriptLang").value===n.scriptLanguage.JS),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.scriptConfigForm.get("scriptLang").value===n.scriptLanguage.TBEL),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(7,5,n.testScriptLabel)," "))},dependencies:t.ɵɵgetComponentDepsFactory(gp),encapsulation:2})}}e("TransformScriptConfigComponent",gp);const hp=()=>({maxWidth:"820px"});function yp(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.from-template-required")," "))}function bp(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.to-template-required")," "))}function vp(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.subject-template-required")," "))}function xp(e,n){if(1&e&&(t.ɵɵelementStart(0,"span"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=t.ɵɵnextContext();t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,e.getBodyTypeName())," ")}}function Cp(e,n){if(1&e&&(t.ɵɵelementStart(0,"mat-option",24)(1,"span",25),t.ɵɵtext(2),t.ɵɵpipe(3,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(4,"br"),t.ɵɵelementStart(5,"small",26),t.ɵɵtext(6),t.ɵɵpipe(7,"translate"),t.ɵɵelementEnd()()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e.value),t.ɵɵadvance(2),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(3,3,e.name)," "),t.ɵɵadvance(4),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(7,5,e.description)," ")}}function Sp(e,n){1&e&&(t.ɵɵelementStart(0,"mat-form-field",18)(1,"mat-label",5),t.ɵɵtext(2,"tb.rulenode.body-type-template"),t.ɵɵelementEnd(),t.ɵɵelement(3,"input",27),t.ɵɵelementStart(4,"mat-hint",5),t.ɵɵtext(5,"tb.mail-body-type.after-template-evaluation-hint"),t.ɵɵelementEnd()())}function Tp(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.body-template-required")," "))}class Ip extends i{constructor(e,t){super(e),this.store=e,this.fb=t,this.mailBodyTypes=[{name:"tb.mail-body-type.plain-text",description:"tb.mail-body-type.plain-text-description",value:"false"},{name:"tb.mail-body-type.html",description:"tb.mail-body-type.html-text-description",value:"true"},{name:"tb.mail-body-type.use-body-type-template",description:"tb.mail-body-type.dynamic-text-description",value:"dynamic"}]}configForm(){return this.toEmailConfigForm}onConfigurationSet(e){this.toEmailConfigForm=this.fb.group({fromTemplate:[e?e.fromTemplate:null,[N.required]],toTemplate:[e?e.toTemplate:null,[N.required]],ccTemplate:[e?e.ccTemplate:null,[]],bccTemplate:[e?e.bccTemplate:null,[]],subjectTemplate:[e?e.subjectTemplate:null,[N.required]],mailBodyType:[e?e.mailBodyType:null],isHtmlTemplate:[e?e.isHtmlTemplate:null,[N.required]],bodyTemplate:[e?e.bodyTemplate:null,[N.required]]})}prepareInputConfig(e){return{fromTemplate:P(e?.fromTemplate)?e.fromTemplate:null,toTemplate:P(e?.toTemplate)?e.toTemplate:null,ccTemplate:P(e?.ccTemplate)?e.ccTemplate:null,bccTemplate:P(e?.bccTemplate)?e.bccTemplate:null,subjectTemplate:P(e?.subjectTemplate)?e.subjectTemplate:null,mailBodyType:P(e?.mailBodyType)?e.mailBodyType:null,isHtmlTemplate:P(e?.isHtmlTemplate)?e.isHtmlTemplate:null,bodyTemplate:P(e?.bodyTemplate)?e.bodyTemplate:null}}updateValidators(e){"dynamic"===this.toEmailConfigForm.get("mailBodyType").value?this.toEmailConfigForm.get("isHtmlTemplate").enable({emitEvent:!1}):this.toEmailConfigForm.get("isHtmlTemplate").disable({emitEvent:!1}),this.toEmailConfigForm.get("isHtmlTemplate").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["mailBodyType"]}getBodyTypeName(){return this.mailBodyTypes.find((e=>e.value===this.toEmailConfigForm.get("mailBodyType").value)).name}static{this.ɵfac=function(e){return new(e||Ip)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Ip,selectors:[["tb-transformation-node-to-email-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:61,vars:23,consts:[[1,"tb-form-panel","no-border","no-padding",3,"formGroup"],[1,"tb-form-panel","stroked"],["translate","",1,"tb-form-panel-title"],[1,"tb-form-row","no-border","no-padding","tb-standard-fields"],["subscriptSizing","dynamic",1,"flex"],["translate",""],["required","","matInput","","formControlName","fromTemplate"],["align","start"],["align","end"],[1,"input-bottom-double-hint"],["hintMode","","tb-help-popup-placement","right","trigger-style","letter-spacing:0.25px; font-size:12px;",1,"see-example",3,"tb-help-popup","tb-help-popup-style","trigger-text"],[4,"ngIf"],[1,"tb-form-panel","no-padding","no-border"],["popupHelpLink","rulenode/to_email_node_fields_templatization",3,"hintText"],[1,"flex"],["required","","matInput","","formControlName","toTemplate","cdkTextareaAutosize","","cdkAutosizeMinRows","1",1,"tb-enable-vertical-resize"],["matInput","","formControlName","ccTemplate","cdkTextareaAutosize","","cdkAutosizeMinRows","1",1,"tb-enable-vertical-resize"],["matInput","","formControlName","bccTemplate","cdkTextareaAutosize","","cdkAutosizeMinRows","1",1,"tb-enable-vertical-resize"],[1,"mat-block"],["required","","matInput","","formControlName","subjectTemplate","cdkTextareaAutosize","","cdkAutosizeMinRows","1",1,"tb-enable-vertical-resize"],["formControlName","mailBodyType"],[3,"value",4,"ngFor","ngForOf"],["class","mat-block",4,"ngIf"],["required","","matInput","","formControlName","bodyTemplate","cdkTextareaAutosize","","cdkAutosizeMinRows","2",1,"tb-enable-vertical-resize"],[3,"value"],["matListItemTitle",""],["matListItemMeta","",2,"color","inherit"],["required","","matInput","","formControlName","isHtmlTemplate"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1)(2,"div",2),t.ɵɵtext(3,"tb.rulenode.email-sender"),t.ɵɵelementEnd(),t.ɵɵelementStart(4,"div",3)(5,"mat-form-field",4)(6,"mat-label",5),t.ɵɵtext(7,"tb.rulenode.from-template"),t.ɵɵelementEnd(),t.ɵɵelement(8,"input",6),t.ɵɵelementStart(9,"mat-hint",7),t.ɵɵtext(10),t.ɵɵpipe(11,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(12,"mat-hint",8)(13,"div",9),t.ɵɵelement(14,"div",10),t.ɵɵpipe(15,"translate"),t.ɵɵelementEnd()(),t.ɵɵtemplate(16,yp,3,3,"mat-error",11),t.ɵɵelementEnd()()(),t.ɵɵelementStart(17,"div",1)(18,"div",12)(19,"div",2),t.ɵɵtext(20,"tb.rulenode.recipients"),t.ɵɵelementEnd(),t.ɵɵelement(21,"tb-example-hint",13),t.ɵɵpipe(22,"translate"),t.ɵɵelementEnd(),t.ɵɵelementStart(23,"div",3)(24,"mat-form-field",14)(25,"mat-label",5),t.ɵɵtext(26,"tb.rulenode.to-template"),t.ɵɵelementEnd(),t.ɵɵelement(27,"textarea",15),t.ɵɵtemplate(28,bp,3,3,"mat-error",11),t.ɵɵelementEnd(),t.ɵɵelementStart(29,"mat-form-field",14)(30,"mat-label",5),t.ɵɵtext(31,"tb.rulenode.cc-template"),t.ɵɵelementEnd(),t.ɵɵelement(32,"textarea",16),t.ɵɵelementEnd(),t.ɵɵelementStart(33,"mat-form-field",14)(34,"mat-label",5),t.ɵɵtext(35,"tb.rulenode.bcc-template"),t.ɵɵelementEnd(),t.ɵɵelement(36,"textarea",17),t.ɵɵelementEnd()()(),t.ɵɵelementStart(37,"div",1)(38,"div",2),t.ɵɵtext(39,"tb.rulenode.message-subject-and-content"),t.ɵɵelementEnd(),t.ɵɵelement(40,"tb-example-hint",13),t.ɵɵpipe(41,"translate"),t.ɵɵelementStart(42,"section")(43,"mat-form-field",18)(44,"mat-label",5),t.ɵɵtext(45,"tb.rulenode.subject-template"),t.ɵɵelementEnd(),t.ɵɵelement(46,"textarea",19),t.ɵɵtemplate(47,vp,3,3,"mat-error",11),t.ɵɵelementEnd(),t.ɵɵelementStart(48,"mat-form-field",18)(49,"mat-label",5),t.ɵɵtext(50,"tb.rulenode.mail-body-type"),t.ɵɵelementEnd(),t.ɵɵelementStart(51,"mat-select",20)(52,"mat-select-trigger"),t.ɵɵtemplate(53,xp,3,3,"span",11),t.ɵɵelementEnd(),t.ɵɵtemplate(54,Cp,8,7,"mat-option",21),t.ɵɵelementEnd()(),t.ɵɵtemplate(55,Sp,6,0,"mat-form-field",22),t.ɵɵelementStart(56,"mat-form-field",18)(57,"mat-label",5),t.ɵɵtext(58,"tb.rulenode.body-template"),t.ɵɵelementEnd(),t.ɵɵelement(59,"textarea",23),t.ɵɵtemplate(60,Tp,3,3,"mat-error",11),t.ɵɵelementEnd()()()()),2&e&&(t.ɵɵproperty("formGroup",n.toEmailConfigForm),t.ɵɵadvance(10),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(11,14,"tb.rulenode.email-from-template-hint")," "),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("tb-help-popup","rulenode/to_email_node_fields_templatization"),t.ɵɵpropertyInterpolate("trigger-text",t.ɵɵpipeBind1(15,16,"tb.key-val.see-examples")),t.ɵɵproperty("tb-help-popup-style",t.ɵɵpureFunction0(22,hp)),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.toEmailConfigForm.get("fromTemplate").hasError("required")),t.ɵɵadvance(5),t.ɵɵproperty("hintText",t.ɵɵpipeBind1(22,18,"tb.rulenode.recipients-block-main-hint")),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",n.toEmailConfigForm.get("toTemplate").hasError("required")),t.ɵɵadvance(12),t.ɵɵproperty("hintText",t.ɵɵpipeBind1(41,20,"tb.rulenode.kv-map-pattern-hint")),t.ɵɵadvance(7),t.ɵɵproperty("ngIf",n.toEmailConfigForm.get("subjectTemplate").hasError("required")),t.ɵɵadvance(6),t.ɵɵproperty("ngIf",n.toEmailConfigForm.get("mailBodyType").value),t.ɵɵadvance(),t.ɵɵproperty("ngForOf",n.mailBodyTypes),t.ɵɵadvance(),t.ɵɵproperty("ngIf","dynamic"===n.toEmailConfigForm.get("mailBodyType").value),t.ɵɵadvance(5),t.ɵɵproperty("ngIf",n.toEmailConfigForm.get("bodyTemplate").hasError("required")))},dependencies:t.ɵɵgetComponentDepsFactory(Ip),styles:["[_nghost-%COMP%] .input-bottom-double-hint[_ngcontent-%COMP%]{display:inline-flex}[_nghost-%COMP%] .input-bottom-double-hint[_ngcontent-%COMP%] .see-example[_ngcontent-%COMP%]{flex-shrink:0;padding-right:16px}[_nghost-%COMP%] textarea.tb-enable-vertical-resize[_ngcontent-%COMP%]{resize:vertical}"]})}}e("ToEmailConfigComponent",Ip);class Ep extends i{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.copyFrom=[],this.translation=Ut;for(const e of this.translation.keys())this.copyFrom.push({value:e,name:this.translate.instant(this.translation.get(e))})}onConfigurationSet(e){this.copyKeysConfigForm=this.fb.group({copyFrom:[e.copyFrom,[N.required]],keys:[e?e.keys:null,[N.required]]})}configForm(){return this.copyKeysConfigForm}prepareInputConfig(e){let t;return t=P(e?.fromMetadata)?e.copyFrom?Kt.METADATA:Kt.DATA:P(e?.copyFrom)?e.copyFrom:Kt.DATA,{keys:P(e?.keys)?e.keys:null,copyFrom:t}}static{this.ɵfac=function(e){return new(e||Ep)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.FormBuilder),t.ɵɵdirectiveInject(K.TranslateService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Ep,selectors:[["tb-transformation-node-copy-keys-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:10,vars:17,consts:[[1,"tb-form-panel","no-padding","no-border",3,"formGroup"],["formControlName","copyFrom",3,"labelText","translation"],["required","","formControlName","keys",1,"mat-block",3,"label","placeholder","requiredText"],["matSuffix","","aria-hidden","false","aria-label","help-icon","color","primary",1,"help-icon","margin-8","cursor-pointer",3,"matTooltip"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0),t.ɵɵelement(1,"tb-msg-metadata-chip",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementStart(3,"tb-string-items-list",2),t.ɵɵpipe(4,"translate"),t.ɵɵpipe(5,"translate"),t.ɵɵpipe(6,"translate"),t.ɵɵelementStart(7,"mat-icon",3),t.ɵɵpipe(8,"translate"),t.ɵɵtext(9," help "),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.copyKeysConfigForm),t.ɵɵadvance(),t.ɵɵproperty("labelText",t.ɵɵpipeBind1(2,7,"tb.key-val.copy-key-values-from"))("translation",n.translation),t.ɵɵadvance(2),t.ɵɵproperty("label",t.ɵɵpipeBind1(4,9,"tb.rulenode.keys"))("placeholder",t.ɵɵpipeBind1(5,11,"tb.rulenode.add-key"))("requiredText",t.ɵɵpipeBind1(6,13,"tb.key-val.at-least-one-key-error")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(8,15,"tb.rulenode.use-regular-expression-hint")))},dependencies:t.ɵɵgetComponentDepsFactory(Ep),encapsulation:2})}}function Fp(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",7),t.ɵɵtext(1),t.ɵɵelementEnd()),2&e){const e=n.$implicit;t.ɵɵproperty("value",e.value),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",e.name," ")}}e("CopyKeysConfigComponent",Ep);class qp extends i{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.renameIn=[],this.translation=zt;for(const e of this.translation.keys())this.renameIn.push({value:e,name:this.translate.instant(this.translation.get(e))})}configForm(){return this.renameKeysConfigForm}onConfigurationSet(e){this.renameKeysConfigForm=this.fb.group({renameIn:[e?e.renameIn:null,[N.required]],renameKeysMapping:[e?e.renameKeysMapping:null,[N.required]]})}prepareInputConfig(e){let t;return t=P(e?.fromMetadata)?e.fromMetadata?Kt.METADATA:Kt.DATA:P(e?.renameIn)?e?.renameIn:Kt.DATA,{renameKeysMapping:P(e?.renameKeysMapping)?e.renameKeysMapping:null,renameIn:t}}static{this.ɵfac=function(e){return new(e||qp)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.FormBuilder),t.ɵɵdirectiveInject(K.TranslateService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:qp,selectors:[["tb-transformation-node-rename-keys-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:15,vars:24,consts:[[1,"tb-form-panel","stroked",3,"formGroup"],["translate","",1,"tb-form-panel-title"],[1,"fx-centered"],[1,"fetch-to-data-toggle"],["formControlName","renameIn","appearance","fill",1,"fetch-to-data-toggle"],[3,"value",4,"ngFor","ngForOf"],["required","","formControlName","renameKeysMapping","uniqueKeyValuePairValidator","",3,"labelText","requiredText","keyText","keyRequiredText","valText","valRequiredText"],[3,"value"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1),t.ɵɵtext(2,"tb.rulenode.rename-keys-in"),t.ɵɵelementEnd(),t.ɵɵelementStart(3,"div",2)(4,"div",3)(5,"tb-toggle-select",4),t.ɵɵtemplate(6,Fp,2,2,"tb-toggle-option",5),t.ɵɵelementEnd()()(),t.ɵɵelement(7,"tb-kv-map-config",6),t.ɵɵpipe(8,"translate"),t.ɵɵpipe(9,"translate"),t.ɵɵpipe(10,"translate"),t.ɵɵpipe(11,"translate"),t.ɵɵpipe(12,"translate"),t.ɵɵpipe(13,"translate"),t.ɵɵpipe(14,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.renameKeysConfigForm),t.ɵɵadvance(6),t.ɵɵproperty("ngForOf",n.renameIn),t.ɵɵadvance(),t.ɵɵpropertyInterpolate2("labelText","",t.ɵɵpipeBind1(8,10,n.translation.get(n.renameKeysConfigForm.get("renameIn").value))," ",t.ɵɵpipeBind1(9,12,"tb.rulenode.keys-mapping"),""),t.ɵɵpropertyInterpolate("requiredText",t.ɵɵpipeBind1(10,14,"tb.rulenode.attr-mapping-required")),t.ɵɵpropertyInterpolate("keyText",t.ɵɵpipeBind1(11,16,"tb.rulenode.current-key-name")),t.ɵɵpropertyInterpolate("keyRequiredText",t.ɵɵpipeBind1(12,18,"tb.rulenode.key-name-required")),t.ɵɵpropertyInterpolate("valText",t.ɵɵpipeBind1(13,20,"tb.rulenode.new-key-name")),t.ɵɵpropertyInterpolate("valRequiredText",t.ɵɵpipeBind1(14,22,"tb.rulenode.new-key-name-required")))},dependencies:t.ɵɵgetComponentDepsFactory(qp),styles:["[_nghost-%COMP%] .fetch-to-data-toggle[_ngcontent-%COMP%]{max-width:420px;width:100%}[_nghost-%COMP%] .fx-centered[_ngcontent-%COMP%]{display:flex;width:100%;justify-content:space-around}"]})}}function Ap(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(2,1,"tb.rulenode.json-path-expression-required")))}e("RenameKeysConfigComponent",qp);class kp extends i{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.jsonPathConfigForm}onConfigurationSet(e){this.jsonPathConfigForm=this.fb.group({jsonPath:[e?e.jsonPath:null,[N.required]]})}static{this.ɵfac=function(e){return new(e||kp)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:kp,selectors:[["tb-transformation-node-json-path-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:10,vars:8,consts:[[3,"formGroup"],["subscriptSizing","dynamic",1,"mat-block"],["matInput","","formControlName","jsonPath","required",""],[4,"ngIf"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"mat-form-field",1)(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"input",2),t.ɵɵelementStart(6,"mat-hint"),t.ɵɵtext(7),t.ɵɵpipe(8,"translate"),t.ɵɵelementEnd(),t.ɵɵtemplate(9,Ap,3,3,"mat-error",3),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.jsonPathConfigForm),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,4,"tb.rulenode.json-path-expression")),t.ɵɵadvance(4),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(8,6,"tb.rulenode.json-path-expression-hint")),t.ɵɵadvance(2),t.ɵɵproperty("ngIf",n.jsonPathConfigForm.get("jsonPath").hasError("required")))},dependencies:t.ɵɵgetComponentDepsFactory(kp),encapsulation:2})}}e("NodeJsonPathConfigComponent",kp);class Np extends i{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.deleteFrom=[],this.translation=Ht;for(const e of this.translation.keys())this.deleteFrom.push({value:e,name:this.translate.instant(this.translation.get(e))})}onConfigurationSet(e){this.deleteKeysConfigForm=this.fb.group({deleteFrom:[e.deleteFrom,[N.required]],keys:[e?e.keys:null,[N.required]]})}prepareInputConfig(e){let t;return t=P(e?.fromMetadata)?e.fromMetadata?Kt.METADATA:Kt.DATA:P(e?.deleteFrom)?e?.deleteFrom:Kt.DATA,{keys:P(e?.keys)?e.keys:null,deleteFrom:t}}configForm(){return this.deleteKeysConfigForm}static{this.ɵfac=function(e){return new(e||Np)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.FormBuilder),t.ɵɵdirectiveInject(K.TranslateService))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Np,selectors:[["tb-transformation-node-delete-keys-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:10,vars:16,consts:[[1,"tb-form-panel","no-border","no-padding",3,"formGroup"],["formControlName","deleteFrom",3,"labelText"],["required","","formControlName","keys",1,"mat-block",3,"label","placeholder","requiredText"],["matSuffix","","aria-hidden","false","aria-label","help-icon","color","primary",1,"help-icon","margin-8","cursor-pointer",3,"matTooltip"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0),t.ɵɵelement(1,"tb-msg-metadata-chip",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementStart(3,"tb-string-items-list",2),t.ɵɵpipe(4,"translate"),t.ɵɵpipe(5,"translate"),t.ɵɵpipe(6,"translate"),t.ɵɵelementStart(7,"mat-icon",3),t.ɵɵpipe(8,"translate"),t.ɵɵtext(9," help "),t.ɵɵelementEnd()()()),2&e&&(t.ɵɵproperty("formGroup",n.deleteKeysConfigForm),t.ɵɵadvance(),t.ɵɵproperty("labelText",t.ɵɵpipeBind1(2,6,"tb.key-val.delete-key-values-from")),t.ɵɵadvance(2),t.ɵɵproperty("label",t.ɵɵpipeBind1(4,8,"tb.rulenode.keys"))("placeholder",t.ɵɵpipeBind1(5,10,"tb.rulenode.add-key"))("requiredText",t.ɵɵpipeBind1(6,12,"tb.key-val.at-least-one-key-error")),t.ɵɵadvance(4),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(8,14,"tb.rulenode.use-regular-expression-delete-hint")))},dependencies:t.ɵɵgetComponentDepsFactory(Np),encapsulation:2})}}function wp(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.interval-required")," "))}function Mp(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.interval-min-error")," "))}function Bp(e,n){if(1&e&&(t.ɵɵelementStart(0,"tb-toggle-option",18),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e){const e=n.$implicit,r=t.ɵɵnextContext();t.ɵɵproperty("value",e),t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,2,r.deduplicationStrategiesTranslations.get(e))," ")}}function Vp(e,n){1&e&&(t.ɵɵelement(0,"tb-example-hint",19),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("hintText",t.ɵɵpipeBind1(1,1,"tb.rulenode.strategy-all-hint"))}function Op(e,n){1&e&&(t.ɵɵelement(0,"tb-example-hint",20),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("hintText",t.ɵɵpipeBind1(1,1,"tb.rulenode.strategy-first-hint"))}function Dp(e,n){1&e&&(t.ɵɵelement(0,"tb-example-hint",20),t.ɵɵpipe(1,"translate")),2&e&&t.ɵɵproperty("hintText",t.ɵɵpipeBind1(1,1,"tb.rulenode.strategy-last-hint"))}function Lp(e,n){1&e&&(t.ɵɵelementStart(0,"div"),t.ɵɵelement(1,"tb-output-message-type-autocomplete",21),t.ɵɵelementEnd())}function Pp(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.max-pending-msgs-required")," "))}function Rp(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.max-pending-msgs-max-error")," "))}function _p(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.max-pending-msgs-min-error")," "))}function jp(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.max-retries-required")," "))}function Gp(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.max-retries-max-error")," "))}function Kp(e,n){1&e&&(t.ɵɵelementStart(0,"mat-error"),t.ɵɵtext(1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵadvance(),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(2,1,"tb.rulenode.max-retries-min-error")," "))}e("DeleteKeysConfigComponent",Np);class Up extends i{constructor(e,t){super(e),this.store=e,this.fb=t,this.deduplicationStrategie=St,this.deduplicationStrategies=Object.keys(this.deduplicationStrategie),this.deduplicationStrategiesTranslations=Tt}configForm(){return this.deduplicationConfigForm}onConfigurationSet(e){this.deduplicationConfigForm=this.fb.group({interval:[P(e?.interval)?e.interval:null,[N.required,N.min(1)]],strategy:[P(e?.strategy)?e.strategy:null,[N.required]],outMsgType:[P(e?.outMsgType)?e.outMsgType:null,[N.required]],maxPendingMsgs:[P(e?.maxPendingMsgs)?e.maxPendingMsgs:null,[N.required,N.min(1),N.max(1e3)]],maxRetries:[P(e?.maxRetries)?e.maxRetries:null,[N.required,N.min(0),N.max(100)]]})}prepareInputConfig(e){return e||(e={}),e.outMsgType||(e.outMsgType="POST_TELEMETRY_REQUEST"),super.prepareInputConfig(e)}updateValidators(e){this.deduplicationConfigForm.get("strategy").value===this.deduplicationStrategie.ALL?this.deduplicationConfigForm.get("outMsgType").enable({emitEvent:!1}):this.deduplicationConfigForm.get("outMsgType").disable({emitEvent:!1}),this.deduplicationConfigForm.get("outMsgType").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["strategy"]}static{this.ɵfac=function(e){return new(e||Up)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.FormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Up,selectors:[["tb-action-node-msg-deduplication-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:49,vars:32,consts:[[3,"formGroup"],[1,"mat-block"],["type","number","required","","matInput","","formControlName","interval"],[4,"ngIf"],["matSuffix","","aria-hidden","false","aria-label","help-icon","color","primary",1,"help-icon","margin-8","cursor-pointer",3,"matTooltip"],[1,"tb-form-panel","no-padding","no-border"],[1,"tb-form-panel","stroked"],["translate","",1,"tb-form-panel-title","tb-required"],["formControlName","strategy","appearance","fill",1,"fetch-to-data-toggle"],[3,"value",4,"ngFor","ngForOf"],[3,"hintText",4,"ngIf"],["textAlign","'center'",3,"hintText",4,"ngIf"],[1,"tb-settings"],["translate",""],[1,"tb-form-row","no-border","no-padding","tb-standard-fields"],[1,"flex"],["type","number","required","","matInput","","formControlName","maxPendingMsgs"],["type","number","required","","matInput","","formControlName","maxRetries"],[3,"value"],[3,"hintText"],["textAlign","'center'",3,"hintText"],["required","","formControlName","outMsgType"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"mat-form-field",1)(2,"mat-label"),t.ɵɵtext(3),t.ɵɵpipe(4,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(5,"input",2),t.ɵɵtemplate(6,wp,3,3,"mat-error",3)(7,Mp,3,3,"mat-error",3),t.ɵɵelementStart(8,"mat-icon",4),t.ɵɵpipe(9,"translate"),t.ɵɵtext(10,"help"),t.ɵɵelementEnd()(),t.ɵɵelementStart(11,"div",5)(12,"div",6)(13,"div",7),t.ɵɵtext(14,"tb.rulenode.strategy"),t.ɵɵelementEnd(),t.ɵɵelementStart(15,"tb-toggle-select",8),t.ɵɵtemplate(16,Bp,3,4,"tb-toggle-option",9),t.ɵɵelementEnd(),t.ɵɵtemplate(17,Vp,2,3,"tb-example-hint",10)(18,Op,2,3,"tb-example-hint",11)(19,Dp,2,3,"tb-example-hint",11)(20,Lp,2,0,"div",3),t.ɵɵelementEnd(),t.ɵɵelementStart(21,"section",6)(22,"mat-expansion-panel",12)(23,"mat-expansion-panel-header")(24,"mat-panel-title",13),t.ɵɵtext(25,"tb.rulenode.advanced-settings"),t.ɵɵelementEnd()(),t.ɵɵelementStart(26,"div",14)(27,"mat-form-field",15)(28,"mat-label"),t.ɵɵtext(29),t.ɵɵpipe(30,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(31,"input",16),t.ɵɵtemplate(32,Pp,3,3,"mat-error",3)(33,Rp,3,3,"mat-error",3)(34,_p,3,3,"mat-error",3),t.ɵɵelementStart(35,"mat-icon",4),t.ɵɵpipe(36,"translate"),t.ɵɵtext(37,"help"),t.ɵɵelementEnd()(),t.ɵɵelementStart(38,"mat-form-field",15)(39,"mat-label"),t.ɵɵtext(40),t.ɵɵpipe(41,"translate"),t.ɵɵelementEnd(),t.ɵɵelement(42,"input",17),t.ɵɵtemplate(43,jp,3,3,"mat-error",3)(44,Gp,3,3,"mat-error",3)(45,Kp,3,3,"mat-error",3),t.ɵɵelementStart(46,"mat-icon",4),t.ɵɵpipe(47,"translate"),t.ɵɵtext(48,"help"),t.ɵɵelementEnd()()()()()()()),2&e&&(t.ɵɵproperty("formGroup",n.deduplicationConfigForm),t.ɵɵadvance(3),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(4,20,"tb.rulenode.interval")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.deduplicationConfigForm.get("interval").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.deduplicationConfigForm.get("interval").hasError("min")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(9,22,"tb.rulenode.interval-hint")),t.ɵɵadvance(8),t.ɵɵproperty("ngForOf",n.deduplicationStrategies),t.ɵɵadvance(),t.ɵɵproperty("ngIf","ALL"===n.deduplicationConfigForm.get("strategy").value),t.ɵɵadvance(),t.ɵɵproperty("ngIf","FIRST"===n.deduplicationConfigForm.get("strategy").value),t.ɵɵadvance(),t.ɵɵproperty("ngIf","LAST"===n.deduplicationConfigForm.get("strategy").value),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.deduplicationConfigForm.get("strategy").value===n.deduplicationStrategie.ALL),t.ɵɵadvance(9),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(30,24,"tb.rulenode.max-pending-msgs")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.deduplicationConfigForm.get("maxPendingMsgs").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.deduplicationConfigForm.get("maxPendingMsgs").hasError("max")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.deduplicationConfigForm.get("maxPendingMsgs").hasError("min")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(36,26,"tb.rulenode.max-pending-msgs-hint")),t.ɵɵadvance(5),t.ɵɵtextInterpolate(t.ɵɵpipeBind1(41,28,"tb.rulenode.max-retries")),t.ɵɵadvance(3),t.ɵɵproperty("ngIf",n.deduplicationConfigForm.get("maxRetries").hasError("required")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.deduplicationConfigForm.get("maxRetries").hasError("max")),t.ɵɵadvance(),t.ɵɵproperty("ngIf",n.deduplicationConfigForm.get("maxRetries").hasError("min")),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("matTooltip",t.ɵɵpipeBind1(47,30,"tb.rulenode.max-retries-hint")))},dependencies:t.ɵɵgetComponentDepsFactory(Up),encapsulation:2})}}e("DeduplicationConfigComponent",Up);class Hp{static{this.ɵfac=function(e){return new(e||Hp)}}static{this.ɵmod=t.ɵɵdefineNgModule({type:Hp})}static{this.ɵinj=t.ɵɵdefineInjector({imports:[$,S,xi,sp,gp,Ip,Ep,qp,kp,Np,Up]})}}e("RulenodeCoreConfigTransformModule",Hp),("undefined"==typeof ngJitMode||ngJitMode)&&t.ɵɵsetNgModuleScope(Hp,{declarations:[sp,gp,Ip,Ep,qp,kp,Np,Up],imports:[$,S,xi],exports:[sp,gp,Ip,Ep,qp,kp,Np,Up]});const zp=e=>[e];class $p extends i{constructor(e,t){super(e),this.store=e,this.fb=t,this.entityType=u}configForm(){return this.ruleChainInputConfigForm}onConfigurationSet(e){this.ruleChainInputConfigForm=this.fb.group({forwardMsgToDefaultRuleChain:[!!e&&e?.forwardMsgToDefaultRuleChain,[]],ruleChainId:[e?e.ruleChainId:null,[N.required]]})}static{this.ɵfac=function(e){return new(e||$p)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:$p,selectors:[["tb-flow-node-rule-chain-input-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:8,vars:12,consts:[[1,"flex","flex-col",3,"formGroup"],[1,"tb-form-panel","no-padding","no-border"],[1,"tb-form-row","no-border",3,"tb-hint-tooltip-icon"],["formControlName","forwardMsgToDefaultRuleChain",1,"mat-slide"],["required","","formControlName","ruleChainId",3,"excludeEntityIds","entityType","entitySubtype"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0)(1,"div",1)(2,"div",2),t.ɵɵpipe(3,"translate"),t.ɵɵelementStart(4,"mat-slide-toggle",3),t.ɵɵtext(5),t.ɵɵpipe(6,"translate"),t.ɵɵelementEnd()(),t.ɵɵelement(7,"tb-entity-autocomplete",4),t.ɵɵelementEnd()()),2&e&&(t.ɵɵproperty("formGroup",n.ruleChainInputConfigForm),t.ɵɵadvance(2),t.ɵɵpropertyInterpolate("tb-hint-tooltip-icon",t.ɵɵpipeBind1(3,6,"tb.rulenode.forward-msg-default-rule-chain-tooltip")),t.ɵɵadvance(3),t.ɵɵtextInterpolate1(" ",t.ɵɵpipeBind1(6,8,"tb.rulenode.forward-msg-default-rule-chain")," "),t.ɵɵadvance(2),t.ɵɵproperty("excludeEntityIds",t.ɵɵpureFunction1(10,zp,n.ruleChainId))("entityType",n.entityType.RULE_CHAIN)("entitySubtype",n.ruleChainType))},dependencies:t.ɵɵgetComponentDepsFactory($p),encapsulation:2})}}e("RuleChainInputComponent",$p);class Qp extends i{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.ruleChainOutputConfigForm}onConfigurationSet(e){this.ruleChainOutputConfigForm=this.fb.group({})}static{this.ɵfac=function(e){return new(e||Qp)(t.ɵɵdirectiveInject(A.Store),t.ɵɵdirectiveInject(k.UntypedFormBuilder))}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Qp,selectors:[["tb-flow-node-rule-chain-output-config"]],features:[t.ɵɵInheritDefinitionFeature],decls:3,vars:4,consts:[[1,"flex","flex-col",3,"formGroup"],[3,"innerHTML"]],template:function(e,n){1&e&&(t.ɵɵelementStart(0,"section",0),t.ɵɵelement(1,"div",1),t.ɵɵpipe(2,"translate"),t.ɵɵelementEnd()),2&e&&(t.ɵɵproperty("formGroup",n.ruleChainOutputConfigForm),t.ɵɵadvance(),t.ɵɵpropertyInterpolate("innerHTML",t.ɵɵpipeBind1(2,2,"tb.rulenode.output-node-name-hint"),t.ɵɵsanitizeHtml))},dependencies:t.ɵɵgetComponentDepsFactory(Qp),encapsulation:2})}}e("RuleChainOutputComponent",Qp);class Jp{static{this.ɵfac=function(e){return new(e||Jp)}}static{this.ɵmod=t.ɵɵdefineNgModule({type:Jp})}static{this.ɵinj=t.ɵɵdefineInjector({imports:[$,S,xi,$p,Qp]})}}e("RuleNodeCoreConfigFlowModule",Jp),("undefined"==typeof ngJitMode||ngJitMode)&&t.ɵɵsetNgModuleScope(Jp,{declarations:[$p,Qp],imports:[$,S,xi],exports:[$p,Qp]});class Yp{constructor(){}static{this.ɵfac=function(e){return new(e||Yp)}}static{this.ɵcmp=t.ɵɵdefineComponent({type:Yp,selectors:[["tb-lib-styles-entry"]],standalone:!0,features:[t.ɵɵStandaloneFeature],decls:0,vars:0,template:function(e,t){},styles:['.tb-default tb-rule-node-config .margin-8{margin:8px}.tb-default tb-rule-node-config .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}.tb-default tb-rule-node-config .tb-required:after{content:"*";font-size:16px;color:#000000de}.tb-default tb-rule-node-config .help-icon{color:#000;opacity:.38;padding:unset}.tb-default tb-rule-node-config .help-icon:hover{color:#305680;opacity:unset}.tb-default tb-rule-node-config .same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.tb-default tb-rule-node-config .same-width-component-row{gap:8px}}.tb-default tb-rule-node-config .same-width-component-row>*{flex:1}.tb-default .gap-0{gap:0px}.tb-default .gap-5\\.5{gap:1.375rem}@media (max-width: 599px){.tb-default .xs\\:max-h-full{max-height:100%}}@media (min-width: 960px) and (max-width: 1279px){.tb-default .md\\:max-h-full{max-height:100%}}@media (max-width: 959px){.tb-default .lt-md\\:gap-4{gap:1rem}}@media (min-width: 960px){.tb-default .gt-sm\\:max-w-10{max-width:2.5rem}.tb-default .gt-sm\\:max-w-10\\%{max-width:10%}.tb-default .gt-sm\\:gap-4{gap:1rem}.tb-default .gt-sm\\:gap-5\\.5{gap:1.375rem}}\n'],encapsulation:2})}}const Wp=(e,t)=>{const n=e[a];if(n.styles?.length){const e=n.styles[0];let r=document.getElementById(t);if(!r){r=document.createElement("style"),r.id=t;(document.head||document.getElementsByTagName("head")[0]).appendChild(r)}r.innerHTML=e}};class Xp{constructor(e){!function(e){e.setTranslation("en_US",{tb:{rulenode:{id:"Id","additional-info":"Additional Info","advanced-settings":"Advanced settings","create-entity-if-not-exists":"Create new entity if it doesn't exist","create-entity-if-not-exists-hint":"If enabled, a new entity with specified parameters will be created unless it already exists. Existing entities will be used as is for relation.","select-device-connectivity-event":"Select device connectivity event","entity-name-pattern":"Name pattern","device-name-pattern":"Device name","asset-name-pattern":"Asset name","entity-view-name-pattern":"Entity view name","customer-title-pattern":"Customer title","dashboard-name-pattern":"Dashboard title","user-name-pattern":"User email","edge-name-pattern":"Edge name","entity-name-pattern-required":"Name pattern is required","entity-name-pattern-hint":"Name pattern field support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","copy-message-type":"Copy message type","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","message-type-value":"Message type value","message-type-value-required":"Message type value is required","message-type-value-max-length":"Message type value should be less than 256","output-message-type":"Output message type","entity-cache-expiration":"Entities cache expiration time (sec)","entity-cache-expiration-hint":"Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.","entity-cache-expiration-required":"Entities cache expiration time is required.","entity-cache-expiration-range":"Entities cache expiration time should be greater than or equal to 0.","customer-name-pattern":"Customer title","customer-name-pattern-required":"Customer title is required","customer-name-pattern-hint":"Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","create-customer-if-not-exists":"Create new customer if it doesn't exist","unassign-from-customer":"Unassign from specific customer if originator is dashboard","unassign-from-customer-tooltip":"Only dashboards can be assigned to multiple customers at once. \nIf the message originator is a dashboard, you need to explicitly specify the customer's title to unassign from.","customer-cache-expiration":"Customers cache expiration time (sec)","customer-cache-expiration-hint":"Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.","customer-cache-expiration-required":"Customers cache expiration time is required.","customer-cache-expiration-range":"Customers cache expiration time should be greater than or equal to 0.","interval-start":"Interval start","interval-end":"Interval end","time-unit":"Time unit","fetch-mode":"Fetch mode","order-by-timestamp":"Order by timestamp",limit:"Limit","limit-hint":"Min limit value is 2, max - 1000. If you want to fetch a single entry, select fetch mode 'First' or 'Last'.","limit-required":"Limit is required.","limit-range":"Limit should be in a range from 2 to 1000.","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Allowing range from 1 to 2147483647.","start-interval-value-required":"Interval start is required.","end-interval-value-required":"Interval end is required.",filter:"Filter",switch:"Switch","math-templatization-tooltip":"This field support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","add-message-type":"Add message type","select-message-types-required":"At least one message type should be selected.","select-message-types":"Select message types","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one.","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","attributes-keys":"Attributes keys","attributes-keys-required":"Attributes keys are required","attributes-scope":"Attributes scope","attributes-scope-value":"Attributes scope value","attributes-scope-value-copy":"Copy attributes scope value","attributes-scope-hint":"Use the 'scope' metadata key to dynamically set the attribute scope per message. If provided, this overrides the scope set in the configuration.","notify-device":"Force notification to the device","send-attributes-updated-notification":"Send attributes updated notification","send-attributes-updated-notification-hint":"Send notification about updated attributes as a separate message to the rule engine queue.","send-attributes-deleted-notification":"Send attributes deleted notification","send-attributes-deleted-notification-hint":"Send notification about deleted attributes as a separate message to the rule engine queue.","update-attributes-only-on-value-change":"Save attributes only if the value changes","update-attributes-only-on-value-change-hint":"Updates the attributes on every incoming message disregarding if their value has changed. Increases API usage and reduces performance.","update-attributes-only-on-value-change-hint-enabled":"Updates the attributes only if their value has changed. If the value is not changed, no update to the attribute timestamp nor attribute change notification will be sent.","fetch-credentials-to-metadata":"Fetch credentials to metadata","notify-device-on-update-hint":"If enabled, force notification to the device about shared attributes update. If disabled, the notification behavior is controlled by the 'notifyDevice' parameter from the incoming message metadata. To turn off the notification, the message metadata must contain the 'notifyDevice' parameter set to 'false'. Any other case will trigger the notification to the device.","notify-device-on-delete-hint":"If enabled, force notification to the device about shared attributes removal. If disabled, the notification behavior is controlled by the 'notifyDevice' parameter from the incoming message metadata. To turn on the notification, the message metadata must contain the 'notifyDevice' parameter set to 'true'. In any other case, the notification will not be triggered to the device.","latest-timeseries":"Latest time series data keys","timeseries-keys":"Time series keys","timeseries-keys-required":"At least one time series key should be selected.","add-timeseries-key":"Add time series key","add-message-field":"Add message field","relation-search-parameters":"Relation search parameters","relation-parameters":"Relation parameters","add-metadata-field":"Add metadata field","data-keys":"Message field names","copy-from":"Copy from","data-to-metadata":"Data to metadata","metadata-to-data":"Metadata to data","use-regular-expression-hint":"Use regular expression to copy keys by pattern.\n\nTips & tricks:\nPress 'Enter' to complete field name input.\nPress 'Backspace' to delete field name. Multiple field names supported.",interval:"Interval","interval-required":"Interval is required","interval-hint":"Deduplication interval in seconds.","interval-min-error":"Min allowed value is 1","max-pending-msgs":"Max pending messages","max-pending-msgs-hint":"Maximum number of messages that are stored in memory for each unique deduplication id.","max-pending-msgs-required":"Max pending messages is required","max-pending-msgs-max-error":"Max allowed value is 1000","max-pending-msgs-min-error":"Min allowed value is 1","max-retries":"Max retries","max-retries-required":"Max retries is required","max-retries-hint":"Maximum number of retries to push the deduplicated messages into the queue. 10 seconds delay is used between retries","max-retries-max-error":"Max allowed value is 100","max-retries-min-error":"Min allowed value is 0",strategy:"Strategy","strategy-required":"Strategy is required","strategy-all-hint":"Return all messages that arrived during deduplication period as a single JSON array message. Where each element represents object with msg and metadata inner properties.","strategy-first-hint":"Return first message that arrived during deduplication period.","strategy-last-hint":"Return last message that arrived during deduplication period.",first:"First",last:"Last",all:"All","output-msg-type-hint":"The message type of the deduplication result.","queue-name-hint":"The queue name where the deduplication result will be published.",keys:"Keys","keys-required":"Keys are required","rename-keys-in":"Rename keys in",data:"Data",message:"Message",metadata:"Metadata","current-key-name":"Current key name","key-name-required":"Key name is required","new-key-name":"New key name","new-key-name-required":"New key name is required","metadata-keys":"Metadata field names","json-path-expression":"JSON path expression","json-path-expression-required":"JSON path expression is required","json-path-expression-hint":"JSONPath specifies a path to an element or a set of elements in a JSON structure. '$' represents the root object or array.","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","max-relation-level-error":"Value should be greater than 0 or unspecified.","max-relation-level-invalid":"Value should be an integer.","relation-type":"Relation type","relation-type-pattern":"Relation type pattern","relation-type-pattern-required":"Relation type pattern is required","relation-types-list":"Relation types to propagate","relation-types-list-hint":"If Propagate relation types are not selected, alarms will be propagated without filtering by relation type.","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","add-telemetry-key":"Add telemetry key","delete-from":"Delete from","use-regular-expression-delete-hint":"Use regular expression to delete keys by pattern.\n\nTips & tricks:\nPress 'Enter' to complete field name input.\nPress 'Backspace' to delete field name.\nMultiple field names supported.","fetch-into":"Fetch into","attr-mapping":"Attributes mapping:","source-attribute":"Source attribute key","source-attribute-required":"Source attribute key is required.","source-telemetry":"Source telemetry key","source-telemetry-required":"Source telemetry key is required.","target-key":"Target key","target-key-required":"Target key is required.","attr-mapping-required":"At least one mapping entry should be specified.","fields-mapping":"Fields mapping","fields-mapping-hint":"If the message field is set to $entityId, the message originator's id will be saved to the specified table column.","relations-query-config-direction-suffix":"originator","profile-name":"Profile name","fetch-circle-parameter-info-from-metadata-hint":'Metadata field \'{{perimeterKeyName}}\' should be defined in next format: {"latitude":48.196, "longitude":24.6532, "radius":100.0, "radiusUnit":"METER"}',"fetch-poligon-parameter-info-from-metadata-hint":"Metadata field '{{perimeterKeyName}}' should be defined in next format: [[48.19736,24.65235],[48.19800,24.65060],...,[48.19849,24.65420]]","short-templatization-tooltip":"Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","fields-mapping-required":"At least one field mapping should be specified.","at-least-one-field-required":"At least one input field must have a value(s) provided.","originator-fields-sv-map-hint":"Target key fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","sv-map-hint":"Only target key fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","new-originator":"New originator","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related entity","originator-alarm-originator":"Alarm Originator","originator-entity":"Entity by name pattern","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","default-ttl-hint":"Rule node will fetch Time-to-Live (TTL) value from the message metadata. If no value is present, it defaults to the TTL specified in the configuration. If the value is set to 0, the TTL from the tenant profile configuration will be applied.","default-ttl-zero-hint":"TTL will not be applied if its value is set to 0.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","generation-parameters":"Generation parameters","message-count":"Generated messages limit (0 - unlimited)","message-count-required":"Generated messages limit is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","generation-frequency-seconds":"Generation frequency in seconds","generation-frequency-required":"Generation frequency is required.","min-generation-frequency-message":"Only 1 second minimum is allowed.","script-lang-tbel":"TBEL","script-lang-js":"JS","use-metadata-period-in-seconds-patterns":"Use period in seconds pattern","use-metadata-period-in-seconds-patterns-hint":"If selected, rule node use period in seconds interval pattern from message metadata or data assuming that intervals are in the seconds.","period-in-seconds-pattern":"Period in seconds pattern","period-in-seconds-pattern-required":"Period in seconds pattern is required","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","current-rule-node":"Current Rule Node","current-tenant":"Current Tenant","generator-function":"Generator function","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","select-entity-types":"Select entity types","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required","alarm-severity-pattern":"Alarm severity pattern","alarm-status-filter":"Alarm status filter","alarm-status-list-empty":"Alarm status list is empty","no-alarm-status-matching":"No alarm status matching were found.",propagate:"Propagate alarm to related entities","propagate-to-owner":"Propagate alarm to entity owner (Customer or Tenant)","propagate-to-tenant":"Propagate alarm to Tenant",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From","from-template-required":"From is required","message-to-metadata":"Message to metadata","metadata-to-message":"Metadata to message","from-message":"From message","from-metadata":"From metadata","to-template":"To","to-template-required":"To Template is required","mail-address-list-template-hint":'Comma separated address list, use ${metadataKey} for value from metadata, $[messageKey] for value from message body',"cc-template":"Cc","bcc-template":"Bcc","subject-template":"Subject","subject-template-required":"Subject Template is required","body-template":"Body","body-template-required":"Body Template is required","dynamic-mail-body-type":"Dynamic mail body type","mail-body-type":"Mail body type","body-type-template":"Body type template","reply-routing-configuration":"Reply Routing Configuration","rpc-reply-routing-configuration-hint":"These configuration parameters specify the metadata key names used to identify the service, session, and request for sending a reply back.","reply-routing-configuration-hint":"These configuration parameters specify the metadata key names used to identify the service and request for sending a reply back.","request-id-metadata-attribute":"Request Id","service-id-metadata-attribute":"Service Id","session-id-metadata-attribute":"Session Id","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory","ignore-request-body":"Without request body","parse-to-plain-text":"Parse to plain text","parse-to-plain-text-hint":'If selected, request body message payload will be transformed from JSON string to plain text, e.g. msg = "Hello,\\t\\"world\\"" will be parsed to Hello, "world"',"read-timeout":"Read timeout in millis","read-timeout-hint":"The value of 0 means an infinite timeout","max-parallel-requests-count":"Max number of parallel requests","max-parallel-requests-count-hint":"The value of 0 specifies no limit in parallel processing","max-response-size":"Max response size (in KB)","max-response-size-hint":"The maximum amount of memory allocated for buffering data when decoding or encoding HTTP messages, such as JSON or XML payloads",headers:"Headers","headers-hint":'Use ${metadataKey} for value from metadata, $[messageKey] for value from message body in header/value fields',header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","key-pattern":"Key pattern","key-pattern-hint":"Optional. If a valid partition number is specified, it will be used when sending the record. If no partition is specified, the key will be used instead. If neither is specified, a partition will be assigned in a round-robin fashion.","topic-pattern-required":"Topic pattern is required",topic:"Topic","topic-required":"Topic is required","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.","memory-buffer-size-range":"Memory buffer size must be between 0 and {{max}} KB",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","gcp-project-id":"GCP project ID","gcp-project-id-required":"GCP project ID is required","gcp-service-account-key":"GCP service account key file","gcp-service-account-key-required":"GCP service account key file is required","pubsub-topic-name":"Topic name","pubsub-topic-name-required":"Topic name is required","message-attributes":"Message attributes","message-attributes-hint":'Use ${metadataKey} for value from metadata, $[messageKey] for value from message body in name/value fields',"connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","client-id-hint":'Optional. Leave empty for auto-generated Client ID. Be careful when specifying the Client ID. Majority of the MQTT brokers will not allow multiple connections with the same Client ID. To connect to such brokers, your mqtt Client ID must be unique. When platform is running in a micro-services mode, the copy of rule node is launched in each micro-service. This will automatically lead to multiple mqtt clients with the same ID and may cause failures of the rule node. To avoid such failures enable "Add Service ID as suffix to Client ID" option below.',"append-client-id-suffix":"Add Service ID as suffix to Client ID","client-id-suffix-hint":'Optional. Applied when "Client ID" specified explicitly. If selected then Service ID will be added to Client ID as a suffix. Helps to avoid failures when platform is running in a micro-services mode.',"device-id":"Device ID","device-id-required":"Device ID is required.","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","credentials-pem-hint":"At least Server CA certificate file or a pair of Client certificate and Client private key files are required","credentials-sas":"Shared Access Signature","sas-key":"SAS Key","sas-key-required":"SAS Key is required.",hostname:"Hostname","hostname-required":"Hostname is required.","azure-ca-cert":"CA certificate file","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"Server CA certificate file","private-key":"Client private key file",cert:"Client certificate file","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","use-metadata-dynamic-interval":"Use dynamic interval","metadata-dynamic-interval-hint":"Interval start and end input fields support templatization. Note that the substituted template value should be set in milliseconds. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata or data assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","overwrite-alarm-details":"Overwrite alarm details","use-alarm-severity-pattern":"Use alarm severity pattern","check-all-keys":"Check that all specified fields are present","check-all-keys-hint":"If selected, checks that all specified keys are present in the message data and metadata.","check-relation-to-specific-entity":"Check relation to specific entity","check-relation-to-specific-entity-tooltip":"If enabled, checks the presence of relation with a specific entity otherwise, checks the presence of relation with any entity. In both cases, relation lookup is based on configured direction and type.","check-relation-hint":"Checks existence of relation to specific entity or to any entity based on direction and relation type.","delete-relation-with-specific-entity":"Delete relation with specific entity","delete-relation-with-specific-entity-hint":"If enabled, will delete the relation with just one specific entity. Otherwise, the relation will be removed with all matching entities.","delete-relation-hint":"Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.","remove-current-relations":"Remove current relations","remove-current-relations-hint":"Removes current relations from the originator of the incoming message based on direction and type.","change-originator-to-related-entity":"Change originator to related entity","change-originator-to-related-entity-hint":"Used to process submitted message as a message from another entity.","start-interval":"Interval start","end-interval":"Interval end","start-interval-required":"Interval start is required.","end-interval-required":"Interval end is required.","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS","tls-version":"TLS version","enable-proxy":"Enable proxy","use-system-proxy-properties":"Use system proxy properties","proxy-host":"Proxy host","proxy-host-required":"Proxy host is required.","proxy-port":"Proxy port","proxy-port-required":"Proxy port is required.","proxy-port-range":"Proxy port should be in a range from 1 to 65535.","proxy-user":"Proxy user","proxy-password":"Proxy password","proxy-scheme":"Proxy scheme","numbers-to-template":"Phone Numbers To Template","numbers-to-template-required":"Phone Numbers To Template is required","numbers-to-template-hint":'Comma separated Phone Numbers, use ${metadataKey} for value from metadata, $[messageKey] for value from message body',"sms-message-template":"SMS message Template","sms-message-template-required":"SMS message Template is required","use-system-sms-settings":"Use system SMS provider settings","min-period-0-seconds-message":"Only 0 second minimum period is allowed.","max-pending-messages":"Maximum pending messages","max-pending-messages-required":"Maximum pending messages is required.","max-pending-messages-range":"Maximum pending messages should be in a range from 1 to 100000.","originator-types-filter":"Originator types filter","interval-seconds":"Interval in seconds","interval-seconds-required":"Interval is required.","int-range":"Value must not exceed the maximum integer limit (2147483648)","min-interval-seconds-message":"Only 1 second minimum interval is allowed.","output-timeseries-key-prefix":"Output time series key prefix","output-timeseries-key-prefix-required":"Output time series key prefix required.","separator-hint":'Press "Enter" to complete field input.',"select-details":"Select details","entity-details-id":"Id","entity-details-title":"Title","entity-details-country":"Country","entity-details-state":"State","entity-details-city":"City","entity-details-zip":"Zip","entity-details-address":"Address","entity-details-address2":"Address2","entity-details-additional_info":"Additional Info","entity-details-phone":"Phone","entity-details-email":"Email","email-sender":"Email sender","fields-to-check":"Fields to check","add-detail":"Add detail","check-all-keys-tooltip":"If enabled, checks the presence of all fields listed in the message and metadata field names within the incoming message and its metadata.","fields-to-check-hint":'Press "Enter" to complete field name input. Multiple field names supported.',"entity-details-list-empty":"At least one detail should be selected.","alarm-status":"Alarm status","alarm-required":"At least one alarm status should be selected.","no-entity-details-matching":"No entity details matching were found.","custom-table-name":"Custom table name","custom-table-name-required":"Table Name is required","custom-table-hint":"The table must be created in your Cassandra cluster and its name must start with the prefix 'cs_tb_' to avoid the data insertion to the common TB tables. Enter the table name here without the 'cs_tb_' prefix.","message-field":"Message field","message-field-required":"Message field is required.","table-col":"Table column","table-col-required":"Table column is required.","latitude-field-name":"Latitude field name","longitude-field-name":"Longitude field name","latitude-field-name-required":"Latitude field name is required.","longitude-field-name-required":"Longitude field name is required.","fetch-perimeter-info-from-metadata":"Fetch perimeter information from metadata","fetch-perimeter-info-from-metadata-tooltip":"If perimeter type is set to 'Polygon' the value of metadata field '{{perimeterKeyName}}' will be set as perimeter definition without additional parsing of the value. Otherwise, if perimeter type is set to 'Circle' the value of '{{perimeterKeyName}}' metadata field will be parsed to extract 'latitude', 'longitude', 'radius', 'radiusUnit' fields that uses for circle perimeter definition.","perimeter-key-name":"Perimeter key name","perimeter-key-name-hint":"Metadata field name that includes perimeter information.","perimeter-key-name-required":"Perimeter key name is required.","perimeter-circle":"Circle","perimeter-polygon":"Polygon","perimeter-type":"Perimeter type","circle-center-latitude":"Center latitude","circle-center-latitude-required":"Center latitude is required.","circle-center-longitude":"Center longitude","circle-center-longitude-required":"Center longitude is required.","range-unit-meter":"Meter","range-unit-kilometer":"Kilometer","range-unit-foot":"Foot","range-unit-mile":"Mile","range-unit-nautical-mile":"Nautical mile","range-units":"Range units","range-units-required":"Range units is required.",range:"Range","range-required":"Range is required.","polygon-definition":"Polygon definition","polygon-definition-required":"Polygon definition is required.","polygon-definition-hint":"Use the following format for manual definition of polygon: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].","min-inside-duration":"Minimal inside duration","min-inside-duration-value-required":"Minimal inside duration is required","min-inside-duration-time-unit":"Minimal inside duration time unit","min-outside-duration":"Minimal outside duration","min-outside-duration-value-required":"Minimal outside duration is required","min-outside-duration-time-unit":"Minimal outside duration time unit","tell-failure-if-absent":"Tell Failure","tell-failure-if-absent-hint":'If at least one selected key doesn\'t exist the outbound message will report "Failure".',"get-latest-value-with-ts":"Fetch timestamp for the latest telemetry values","get-latest-value-with-ts-hint":'If selected, the latest telemetry values will also include timestamp, e.g: "temp": "{"ts":1574329385897, "value":42}"',"ignore-null-strings":"Ignore null strings","ignore-null-strings-hint":"If selected rule node will ignore entity fields with empty value.","add-metadata-key-values-as-kafka-headers":"Add Message metadata key-value pairs to Kafka record headers","add-metadata-key-values-as-kafka-headers-hint":"If selected, key-value pairs from message metadata will be added to the outgoing records headers as byte arrays with predefined charset encoding.","charset-encoding":"Charset encoding","charset-encoding-required":"Charset encoding is required.","charset-us-ascii":"US-ASCII","charset-iso-8859-1":"ISO-8859-1","charset-utf-8":"UTF-8","charset-utf-16be":"UTF-16BE","charset-utf-16le":"UTF-16LE","charset-utf-16":"UTF-16","select-queue-hint":"The queue name can be selected from a drop-down list or add a custom name.","device-profile-node-hint":"Useful if you have duration or repeating conditions to ensure continuity of alarm state evaluation.","persist-alarm-rules":"Persist state of alarm rules","persist-alarm-rules-hint":"If enabled, the rule node will store the state of processing to the database.","fetch-alarm-rules":"Fetch state of alarm rules","fetch-alarm-rules-hint":"If enabled, the rule node will restore the state of processing on initialization and ensure that alarms are raised even after server restarts. Otherwise, the state will be restored when the first message from the device arrives.","input-value-key":"Input value key","input-value-key-required":"Input value key is required.","output-value-key":"Output value key","output-value-key-required":"Output value key is required.","number-of-digits-after-floating-point":"Number of digits after floating point","number-of-digits-after-floating-point-range":"Number of digits after floating point should be in a range from 0 to 15.","failure-if-delta-negative":"Tell Failure if delta is negative","failure-if-delta-negative-tooltip":"Rule node forces failure of message processing if delta value is negative.","use-caching":"Use caching","use-caching-tooltip":'Rule node will cache the value of "{{inputValueKey}}" that arrives from the incoming message to improve performance. Note that the cache will not be updated if you modify the "{{inputValueKey}}" value elsewhere.',"add-time-difference-between-readings":'Add the time difference between "{{inputValueKey}}" readings',"add-time-difference-between-readings-tooltip":'If enabled, the rule node will add the "{{periodValueKey}}" to the outbound message.',"period-value-key":"Period value key","period-value-key-required":"Period value key is required.","general-pattern-hint":"Use ${metadataKey} for value from metadata, $[messageKey] for value from message body.","alarm-severity-pattern-hint":'Use ${metadataKey} for value from metadata, $[messageKey] for value from message body. Alarm severity should be system (CRITICAL, MAJOR etc.)',"output-node-name-hint":"The 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.","skip-latest-persistence":"Skip latest persistence","skip-latest-persistence-hint":"Rule node will not update values for incoming keys for the latest time series data. Useful for highly loaded use-cases to decrease the pressure on the DB.","use-server-ts":"Use server ts","use-server-ts-hint":"Rule node will use the timestamp of message processing instead of the timestamp from the message. Useful for all sorts of sequential processing if you merge messages from multiple sources (devices, assets, etc).","kv-map-pattern-hint":"All input fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","kv-map-single-pattern-hint":"Input field support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","shared-scope":"Shared scope","server-scope":"Server scope","client-scope":"Client scope","attribute-type":"Attribute","constant-type":"Constant","time-series-type":"Time series","message-body-type":"Message","message-metadata-type":"Metadata","argument-tile":"Arguments","no-arguments-prompt":"No arguments configured","result-title":"Result","functions-field-input":"Functions","no-option-found":"No option found","argument-source-field-input":"Source","argument-source-field-input-required":"Argument source is required.","argument-key-field-input":"Key","argument-key-field-input-required":"Argument key is required.","constant-value-field-input":"Constant value","constant-value-field-input-required":"Constant value is required.","attribute-scope-field-input":"Attribute scope","attribute-scope-field-input-required":"Attribute scope os required.","default-value-field-input":"Default value","type-field-input":"Type","type-field-input-required":"Type is required.","key-field-input":"Key","add-entity-type":"Add entity type","add-device-profile":"Add device profile","key-field-input-required":"Key is required.","number-floating-point-field-input":"Number of digits after floating point","number-floating-point-field-input-hint":"Use 0 to convert result to integer","add-to-message-field-input":"Add to message","add-to-metadata-field-input":"Add to metadata","custom-expression-field-input":"Mathematical Expression","custom-expression-field-input-required":"Mathematical expression is required","custom-expression-field-input-hint":"Specify a mathematical expression to evaluate. Default expression demonstrates how to transform Fahrenheit to Celsius","retained-message":"Retained","attributes-mapping":"Attributes mapping","latest-telemetry-mapping":"Latest telemetry mapping","add-mapped-attribute-to":"Add mapped attributes to","add-mapped-latest-telemetry-to":"Add mapped latest telemetry to","add-mapped-fields-to":"Add mapped fields to","add-selected-details-to":"Add selected details to","clear-selected-types":"Clear selected types","clear-selected-details":"Clear selected details","clear-selected-fields":"Clear selected fields","clear-selected-keys":"Clear selected keys","geofence-configuration":"Geofence configuration","coordinate-field-names":"Coordinate field names","coordinate-field-hint":"Rule node tries to retrieve the specified fields from the message. If they are not present, it will look them up in the metadata.","presence-monitoring-strategy":"Presence monitoring strategy","presence-monitoring-strategy-on-first-message":"On first message","presence-monitoring-strategy-on-each-message":"On each message","presence-monitoring-strategy-on-first-message-hint":"Reports presence status 'Inside' or 'Outside' on the first message after the configured minimal duration has passed since previous presence status 'Entered' or 'Left' update.","presence-monitoring-strategy-on-each-message-hint":"Reports presence status 'Inside' or 'Outside' on each message after presence status 'Entered' or 'Left' update.","fetch-credentials-to":"Fetch credentials to","add-originator-attributes-to":"Add originator attributes to","originator-attributes":"Originator attributes","fetch-latest-telemetry-with-timestamp":"Fetch latest telemetry with timestamp","fetch-latest-telemetry-with-timestamp-tooltip":'If selected, latest telemetry values will be added to the outbound metadata with timestamp, e.g: "{{latestTsKeyName}}": "{"ts":1574329385897, "value":42}"',"tell-failure":"Tell failure if any of the attributes are missing","tell-failure-tooltip":'If at least one selected key doesn\'t exist the outbound message will report "Failure".',"created-time":"Created time","chip-help":"Press 'Enter' to complete {{inputName}} input. \nPress 'Backspace' to delete {{inputName}}. \nMultiple values supported.",detail:"detail","field-name":"field name","device-profile":"device profile","entity-type":"entity type","message-type":"message type","timeseries-key":"time series key",type:"Type","first-name":"First name","last-name":"Last name",label:"Label","originator-fields-mapping":"Originator fields mapping","add-mapped-originator-fields-to":"Add mapped originator fields to",fields:"Fields","skip-empty-fields":"Skip empty fields","skip-empty-fields-tooltip":"Fields with empty values will not be added to the output message/output metadata.","fetch-interval":"Fetch interval","fetch-strategy":"Fetch strategy","fetch-timeseries-from-to":"Fetch time series from {{startInterval}} {{startIntervalTimeUnit}} ago to {{endInterval}} {{endIntervalTimeUnit}} ago.","fetch-timeseries-from-to-invalid":'Fetch time series invalid ("Interval start" should be less than "Interval end").',"use-metadata-dynamic-interval-tooltip":"If selected, the rule node will use dynamic interval start and end based on the message and metadata patterns.","all-mode-hint":'If selected fetch mode "All" rule node will retrieve telemetry from the fetch interval with configurable query parameters.',"first-mode-hint":'If selected fetch mode "First" rule node will retrieve the closest telemetry to the fetch interval\'s start.',"last-mode-hint":'If selected fetch mode "Last" rule node will retrieve the closest telemetry to the fetch interval\'s end.',ascending:"Ascending",descending:"Descending",min:"Min",max:"Max",average:"Average",sum:"Sum",count:"Count",none:"None","last-level-relation-tooltip":"If selected, the rule node will search related entities only on the level set in the max relation level.","last-level-device-relation-tooltip":"If selected, the rule node will search related devices only on the level set in the max relation level.","data-to-fetch":"Data to fetch","mapping-of-customers":"Mapping of customer's","map-fields-required":"All mapping fields are required.",attributes:"Attributes","related-device-attributes":"Related device attributes","add-selected-attributes-to":"Add selected attributes to","device-profiles":"Device profiles","mapping-of-tenant":"Mapping of tenant's","add-attribute-key":"Add attribute key","message-template":"Message template","message-template-required":"Message template is required","use-system-slack-settings":"Use system slack settings","slack-api-token":"Slack API token","slack-api-token-required":"Slack API token is required","keys-mapping":"keys mapping","add-key":"Add key",recipients:"Recipients","message-subject-and-content":"Message subject and content","template-rules-hint":"Both input fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the message metadata.","originator-customer-desc":"Use customer of incoming message originator as new originator.","originator-tenant-desc":"Use current tenant as new originator.","originator-related-entity-desc":"Use related entity as new originator. Lookup based on configured relation type and direction.","originator-alarm-originator-desc":"Use alarm originator as new originator. Only if incoming message originator is alarm entity.","originator-entity-by-name-pattern-desc":"Use entity fetched from DB as new originator. Lookup based on entity type and specified name pattern.","email-from-template-hint":"Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","recipients-block-main-hint":"Comma-separated address list. All input fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","forward-msg-default-rule-chain":"Forward message to the originator's default rule chain","forward-msg-default-rule-chain-tooltip":"If enabled, message will be forwarded to the originator's default rule chain, or rule chain from configuration, if originator has no default rule chain defined in the entity profile.","exclude-zero-deltas":"Exclude zero deltas from outbound message","exclude-zero-deltas-hint":'If enabled, the "{{outputValueKey}}" output key will be added to the outbound message if its value is not zero.',"exclude-zero-deltas-time-difference-hint":'If enabled, the "{{outputValueKey}}" and "{{periodValueKey}}" output keys will be added to the outbound message only if the "{{outputValueKey}}" value is not zero.',"search-direction-from":"From originator to target entity","search-direction-to":"From target entity to originator","del-relation-direction-from":"From originator","del-relation-direction-to":"To originator","target-entity":"Target entity","function-configuration":"Function configuration","function-name":"Function name","function-name-required":"Function name is required.",qualifier:"Qualifier","qualifier-hint":'If the qualifier is not specified, the default qualifier "$LATEST" will be used.',"aws-credentials":"AWS Credentials","connection-timeout":"Connection timeout","connection-timeout-required":"Connection timeout is required.","connection-timeout-min":"Min connection timeout is 0.","connection-timeout-hint":"The amount of time to wait in seconds when initially establishing a connection before giving up and timing out. A value of 0 means infinity, and is not recommended.","request-timeout":"Request timeout","request-timeout-required":"Request timeout is required","request-timeout-min":"Min request timeout is 0","request-timeout-hint":"The amount of time to wait in seconds for the request to complete before giving up and timing out. A value of 0 means infinity, and is not recommended.","tell-failure-aws-lambda":"Tell Failure if AWS Lambda function execution raises exception","tell-failure-aws-lambda-hint":"Rule node forces failure of message processing if AWS Lambda function execution raises exception."},"key-val":{key:"Key",value:"Value","see-examples":"See examples.","remove-entry":"Remove entry","remove-mapping-entry":"Remove mapping entry","add-mapping-entry":"Add mapping","add-entry":"Add entry","copy-key-values-from":"Copy key-values from","delete-key-values":"Delete key-values","delete-key-values-from":"Delete key-values from","at-least-one-key-error":"At least one key should be selected.","unique-key-value-pair-error":"'{{keyText}}' must be different from the '{{valText}}'!"},"mail-body-type":{"plain-text":"Plain text",html:"HTML",dynamic:"Dynamic","use-body-type-template":"Use body type template","plain-text-description":"Simple, unformatted text with no special styling or formating.","html-text-description":"Allows you to use HTML tags for formatting, links and images in your mai body.","dynamic-text-description":"Allows to use Plain Text or HTML body type dynamically based on templatization feature.","after-template-evaluation-hint":"After template evaluation value should be true for HTML, and false for Plain text."}}},!0)}(e),Wp(Yp,"tb-rule-node-core-config-css")}static{this.ɵfac=function(e){return new(e||Xp)(t.ɵɵinject(K.TranslateService))}}static{this.ɵmod=t.ɵɵdefineNgModule({type:Xp})}static{this.ɵinj=t.ɵɵdefineInjector({imports:[$,S,Ci,np,bo,hs,Hp,Jp,oe]})}}e("RuleNodeCoreConfigModule",Xp),("undefined"==typeof ngJitMode||ngJitMode)&&t.ɵɵsetNgModuleScope(Xp,{declarations:[oe],imports:[$,S],exports:[Ci,np,bo,hs,Hp,Jp,oe]})}}}));//# sourceMappingURL=rulenode-core-config.js.map From 0de04791775d529187366278d2c86b455661711e Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 9 Jan 2025 13:04:40 +0200 Subject: [PATCH 048/108] UI: Add lazy-load rulechain page components --- .../src/app/core/http/rule-chain.service.ts | 10 +++- .../rule-node/rule-node-config.module.ts | 26 +++++---- .../home/pages/edge/edge-routing.module.ts | 3 + .../rulechain/rule-node-config.component.ts | 10 +--- .../pages/rulechain/rulechain-page.module.ts | 57 +++++++++++++++++++ .../rulechain/rulechain-routing.module.ts | 2 + .../home/pages/rulechain/rulechain.module.ts | 25 +------- 7 files changed, 89 insertions(+), 44 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.module.ts diff --git a/ui-ngx/src/app/core/http/rule-chain.service.ts b/ui-ngx/src/app/core/http/rule-chain.service.ts index e3353989cc..37a9f8c1af 100644 --- a/ui-ngx/src/app/core/http/rule-chain.service.ts +++ b/ui-ngx/src/app/core/http/rule-chain.service.ts @@ -53,6 +53,7 @@ export class RuleChainService { private ruleNodeComponentsMap: Map> = new Map>(); private ruleNodeConfigComponents: {[directive: string]: Type} = {}; + private systemRuleNodeConfigComponents: {[directive: string]: Type} = {}; constructor( private http: HttpClient, @@ -128,7 +129,10 @@ export class RuleChainService { } } - public getRuleNodeConfigComponent(directive: string): Type { + public getRuleNodeConfigComponent(directive: string, isSystemComponent = false): Type { + if (isSystemComponent) { + return this.systemRuleNodeConfigComponents[directive]; + } return this.ruleNodeConfigComponents[directive]; } @@ -180,6 +184,10 @@ export class RuleChainService { return this.http.post(url, inputParams, defaultHttpOptionsFromConfig(config)); } + public registemSystemRuleNodeConfigComponent(componentMap: Record>) { + this.systemRuleNodeConfigComponents = componentMap; + } + private loadRuleNodeComponents(ruleChainType: RuleChainType, config?: RequestConfig): Observable> { return this.componentDescriptorService.getComponentDescriptorsByTypes(ruleNodeTypeComponentTypes, ruleChainType, config).pipe( map((components) => { diff --git a/ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.module.ts index 58efd925db..f960777220 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.module.ts @@ -43,6 +43,7 @@ import { FlowRuleNodeConfigModule } from '@home/components/rule-node/flow/flow-rule-node-config.module'; import { IRuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; +import { RuleChainService } from '@core/http/rule-chain.service'; @NgModule({ declarations: [ @@ -62,14 +63,17 @@ import { IRuleNodeConfigurationComponent } from '@shared/models/rule-node.models EmptyConfigComponent ] }) -export class RuleNodeConfigModule {} - -export const ruleNodeConfigComponentsMap: Record> = { - ...actionRuleNodeConfigComponentsMap, - ...enrichmentRuleNodeConfigComponentsMap, - ...externalRuleNodeConfigComponentsMap, - ...filterRuleNodeConfigComponentsMap, - ...flowRuleNodeConfigComponentsMap, - ...transformationRuleNodeConfigComponentsMap, - 'tbNodeEmptyConfig': EmptyConfigComponent -}; +export class RuleNodeConfigModule { + constructor(private ruleChainService: RuleChainService) { + const ruleNodeConfigComponentsMap: Record> = { + ...actionRuleNodeConfigComponentsMap, + ...enrichmentRuleNodeConfigComponentsMap, + ...externalRuleNodeConfigComponentsMap, + ...filterRuleNodeConfigComponentsMap, + ...flowRuleNodeConfigComponentsMap, + ...transformationRuleNodeConfigComponentsMap, + 'tbNodeEmptyConfig': EmptyConfigComponent + }; + this.ruleChainService.registemSystemRuleNodeConfigComponent(ruleNodeConfigComponentsMap); + } +} diff --git a/ui-ngx/src/app/modules/home/pages/edge/edge-routing.module.ts b/ui-ngx/src/app/modules/home/pages/edge/edge-routing.module.ts index 1302bfa140..bef4cd4a51 100644 --- a/ui-ngx/src/app/modules/home/pages/edge/edge-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/edge/edge-routing.module.ts @@ -291,6 +291,7 @@ const routes: Routes = [ import: false, ruleChainType: RuleChainType.EDGE }, + loadChildren: () => import('../rulechain/rulechain-page.module').then(m => m.RuleChainPageModule), resolve: { ruleChain: RuleChainResolver, ruleChainMetaData: RuleChainMetaDataResolver, @@ -336,6 +337,7 @@ const routes: Routes = [ import: false, ruleChainType: RuleChainType.EDGE }, + loadChildren: () => import('../rulechain/rulechain-page.module').then(m => m.RuleChainPageModule), resolve: { ruleChain: RuleChainResolver, ruleChainMetaData: RuleChainMetaDataResolver, @@ -358,6 +360,7 @@ const routes: Routes = [ import: true, ruleChainType: RuleChainType.EDGE }, + loadChildren: () => import('../rulechain/rulechain-page.module').then(m => m.RuleChainPageModule), resolve: { ruleNodeComponents: RuleNodeComponentsResolver, tooltipster: TooltipsterResolver diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-config.component.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-config.component.ts index 029c4b5547..f6ed94991c 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-config.component.ts +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-config.component.ts @@ -21,7 +21,7 @@ import { forwardRef, Input, OnDestroy, - Output, Type, + Output, ViewChild, ViewContainerRef } from '@angular/core'; @@ -43,7 +43,6 @@ import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { JsonObjectEditComponent } from '@shared/components/json-object-edit.component'; import { deepClone } from '@core/utils'; import { RuleChainType } from '@shared/models/rule-chain.models'; -import { ruleNodeConfigComponentsMap } from '@home/components/rule-node/rule-node-config.module'; @Component({ selector: 'tb-rule-node-config', @@ -213,12 +212,7 @@ export class RuleNodeConfigComponent implements ControlValueAccessor, OnDestroy this.changeScriptSubscription = null; } this.definedConfigContainer.clear(); - let component: Type; - if (!this.nodeDefinition.uiResources?.length) { - component = ruleNodeConfigComponentsMap[this.nodeDefinition.configDirective]; - } else { - component = this.ruleChainService.getRuleNodeConfigComponent(this.nodeDefinition.configDirective); - } + const component = this.ruleChainService.getRuleNodeConfigComponent(this.nodeDefinition.configDirective, !this.nodeDefinition.uiResources?.length); this.definedConfigComponentRef = this.definedConfigContainer.createComponent(component); this.definedConfigComponent = this.definedConfigComponentRef.instance; this.definedConfigComponent.ruleNodeId = this.ruleNodeId; diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.module.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.module.ts new file mode 100644 index 0000000000..5e2596939e --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.module.ts @@ -0,0 +1,57 @@ +/// +/// 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/shared.module'; +import { HomeComponentsModule } from '@home/components/home-components.module'; +import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe'; +import { + EntityDebugSettingsButtonComponent +} from '@home/components/entity/debug/entity-debug-settings-button.component'; +import { RuleNodeConfigModule } from '@home/components/rule-node/rule-node-config.module'; +import { + AddRuleNodeDialogComponent, + AddRuleNodeLinkDialogComponent, + CreateNestedRuleChainDialogComponent, + RuleChainPageComponent +} from '@home/pages/rulechain/rulechain-page.component'; +import { RuleNodeDetailsComponent } from '@home/pages/rulechain/rule-node-details.component'; +import { RuleNodeConfigComponent } from '@home/pages/rulechain/rule-node-config.component'; +import { LinkLabelsComponent } from '@home/pages/rulechain/link-labels.component'; +import { RuleNodeLinkComponent } from '@home/pages/rulechain/rule-node-link.component'; + +@NgModule({ + declarations: [ + RuleChainPageComponent, + RuleNodeDetailsComponent, + LinkLabelsComponent, + RuleNodeLinkComponent, + RuleNodeConfigComponent, + AddRuleNodeLinkDialogComponent, + AddRuleNodeDialogComponent, + CreateNestedRuleChainDialogComponent + ], + imports: [ + CommonModule, + SharedModule, + DurationLeftPipe, + EntityDebugSettingsButtonComponent, + RuleNodeConfigModule, + HomeComponentsModule, + ] +}) +export class RuleChainPageModule {} diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-routing.module.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-routing.module.ts index 0162e43655..cff4d2df4e 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-routing.module.ts @@ -148,6 +148,7 @@ const routes: Routes = [ import: false, ruleChainType: RuleChainType.CORE }, + loadChildren: () => import('./rulechain-page.module').then(m => m.RuleChainPageModule), resolve: { ruleChain: RuleChainResolver, ruleChainMetaData: RuleChainMetaDataResolver, @@ -170,6 +171,7 @@ const routes: Routes = [ import: true, ruleChainType: RuleChainType.CORE }, + loadChildren: () => import('./rulechain-page.module').then(m => m.RuleChainPageModule), resolve: { ruleNodeComponents: RuleNodeComponentsResolver, tooltipster: TooltipsterResolver diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts index 00fa560382..be9478574d 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts @@ -21,34 +21,14 @@ import { RuleChainComponent } from '@modules/home/pages/rulechain/rulechain.comp import { RuleChainRoutingModule } from '@modules/home/pages/rulechain/rulechain-routing.module'; import { HomeComponentsModule } from '@modules/home/components/home-components.module'; import { RuleChainTabsComponent } from '@home/pages/rulechain/rulechain-tabs.component'; -import { - AddRuleNodeDialogComponent, - AddRuleNodeLinkDialogComponent, CreateNestedRuleChainDialogComponent, - RuleChainPageComponent -} from './rulechain-page.component'; import { RuleNodeComponent } from '@home/pages/rulechain/rulenode.component'; import { FC_NODE_COMPONENT_CONFIG } from 'ngx-flowchart'; -import { RuleNodeDetailsComponent } from './rule-node-details.component'; -import { RuleNodeLinkComponent } from './rule-node-link.component'; -import { LinkLabelsComponent } from '@home/pages/rulechain/link-labels.component'; -import { RuleNodeConfigComponent } from './rule-node-config.component'; -import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe'; -import { EntityDebugSettingsButtonComponent } from '@home/components/entity/debug/entity-debug-settings-button.component'; -import { RuleNodeConfigModule } from '@home/components/rule-node/rule-node-config.module'; @NgModule({ declarations: [ RuleChainComponent, RuleChainTabsComponent, - RuleChainPageComponent, RuleNodeComponent, - RuleNodeDetailsComponent, - RuleNodeConfigComponent, - LinkLabelsComponent, - RuleNodeLinkComponent, - AddRuleNodeLinkDialogComponent, - AddRuleNodeDialogComponent, - CreateNestedRuleChainDialogComponent ], providers: [ { @@ -56,16 +36,13 @@ import { RuleNodeConfigModule } from '@home/components/rule-node/rule-node-confi useValue: { nodeComponentType: RuleNodeComponent } - } + }, ], imports: [ CommonModule, SharedModule, HomeComponentsModule, RuleChainRoutingModule, - DurationLeftPipe, - EntityDebugSettingsButtonComponent, - RuleNodeConfigModule, ] }) export class RuleChainModule { } From fdd7a82c9e40439ab74dfe1abad2aa02a445b3b7 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Thu, 9 Jan 2025 18:50:45 +0200 Subject: [PATCH 049/108] Fixed empty notifications on slow WS connection --- .../show-notification-popover.component.html | 4 +- .../show-notification-popover.component.ts | 40 ++++++------------- 2 files changed, 15 insertions(+), 29 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/notification/show-notification-popover.component.html b/ui-ngx/src/app/modules/home/components/notification/show-notification-popover.component.html index 0e40d8e6a5..edbff9ce7e 100644 --- a/ui-ngx/src/app/modules/home/components/notification/show-notification-popover.component.html +++ b/ui-ngx/src/app/modules/home/components/notification/show-notification-popover.component.html @@ -18,13 +18,13 @@
    notification.notification
    - +
    diff --git a/ui-ngx/src/app/modules/home/components/notification/show-notification-popover.component.ts b/ui-ngx/src/app/modules/home/components/notification/show-notification-popover.component.ts index 32fcf2dc4a..ad601bd33a 100644 --- a/ui-ngx/src/app/modules/home/components/notification/show-notification-popover.component.ts +++ b/ui-ngx/src/app/modules/home/components/notification/show-notification-popover.component.ts @@ -14,24 +14,25 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, Input, NgZone, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectorRef, Component, Input, NgZone, OnDestroy } from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { TbPopoverComponent } from '@shared/components/popover.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { Notification, NotificationRequest } from '@shared/models/notification.models'; import { NotificationWebsocketService } from '@core/ws/notification-websocket.service'; -import { BehaviorSubject, Observable, ReplaySubject, Subscription } from 'rxjs'; -import { map, share, skip, tap } from 'rxjs/operators'; +import { BehaviorSubject, Observable, shareReplay } from 'rxjs'; +import { filter, skip, tap } from 'rxjs/operators'; import { Router } from '@angular/router'; import { NotificationSubscriber } from '@shared/models/telemetry/telemetry.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-show-notification-popover', templateUrl: './show-notification-popover.component.html', styleUrls: ['show-notification-popover.component.scss'] }) -export class ShowNotificationPopoverComponent extends PageComponent implements OnDestroy, OnInit { +export class ShowNotificationPopoverComponent extends PageComponent implements OnDestroy { @Input() onClose: () => void; @@ -42,11 +43,13 @@ export class ShowNotificationPopoverComponent extends PageComponent implements O @Input() popoverComponent: TbPopoverComponent; - private notificationSubscriber: NotificationSubscriber; - private notificationCountSubscriber: Subscription; + private notificationSubscriber = NotificationSubscriber.createNotificationsSubscription(this.notificationWsService, this.zone, 6); - notifications$: Observable; - loadNotification = false; + notifications$: Observable = this.notificationSubscriber.notifications$.pipe( + filter(value => Array.isArray(value)), + shareReplay(1), + tap(() => setTimeout(() => this.cd.markForCheck())) + ); constructor(protected store: Store, private notificationWsService: NotificationWebsocketService, @@ -54,32 +57,15 @@ export class ShowNotificationPopoverComponent extends PageComponent implements O private cd: ChangeDetectorRef, private router: Router) { super(store); - } - - ngOnInit() { - this.notificationSubscriber = NotificationSubscriber.createNotificationsSubscription(this.notificationWsService, this.zone, 6); - this.notifications$ = this.notificationSubscriber.notifications$.pipe( - map(value => { - if (Array.isArray(value)) { - this.loadNotification = true; - return value; - } - return []; - }), - share({ - connector: () => new ReplaySubject(1) - }), - tap(() => setTimeout(() => this.cd.markForCheck())) - ); - this.notificationCountSubscriber = this.notificationSubscriber.notificationCount$.pipe( + this.notificationSubscriber.notificationCount$.pipe( skip(1), + takeUntilDestroyed() ).subscribe(value => this.counter.next(value)); this.notificationSubscriber.subscribe(); } ngOnDestroy() { super.ngOnDestroy(); - this.notificationCountSubscriber.unsubscribe(); this.notificationSubscriber.unsubscribe(); this.onClose(); } From 000c38f03c7db6d76df794f2e3539f37436c8956 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 10 Jan 2025 19:07:03 +0200 Subject: [PATCH 050/108] Update tb-html-fallback-middleware.ts --- ui-ngx/esbuild/tb-html-fallback-middleware.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/esbuild/tb-html-fallback-middleware.ts b/ui-ngx/esbuild/tb-html-fallback-middleware.ts index bcb86823d3..22acf6b2b7 100644 --- a/ui-ngx/esbuild/tb-html-fallback-middleware.ts +++ b/ui-ngx/esbuild/tb-html-fallback-middleware.ts @@ -23,7 +23,7 @@ const tbHtmlFallbackMiddleware: NextHandleFunction = ( _res: ServerResponse, next: Connect.NextFunction ) => { - if (/^\/resources\/scada-symbols\/(?:system|tenant)\/[^/]+\.svg$/.test(req.url)) { + if (/^\/resources\/scada-symbols\/(?:system|tenant)\/[^/]+$/.test(req.url)) { req.url = '/'; } next(); From 345e4239732c2c801878445cd65ee369b61160cc Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Sun, 12 Jan 2025 14:27:46 +0200 Subject: [PATCH 051/108] Save time series strategies: update time series node config in rule chain JSONs; add null-check for persistence settings --- .../rule_chains/edge_root_rule_chain.json | 8 ++++++-- .../device_profile/rule_chain_template.json | 8 ++++++-- .../tenant/rule_chains/root_rule_chain.json | 6 +++++- .../src/main/resources/root_rule_chain.json | 20 ++++++++++++++----- .../engine/telemetry/TbMsgTimeseriesNode.java | 3 +++ 5 files changed, 35 insertions(+), 10 deletions(-) diff --git a/application/src/main/data/json/edge/rule_chains/edge_root_rule_chain.json b/application/src/main/data/json/edge/rule_chains/edge_root_rule_chain.json index 9adeb4f49e..e7b4b93e98 100644 --- a/application/src/main/data/json/edge/rule_chains/edge_root_rule_chain.json +++ b/application/src/main/data/json/edge/rule_chains/edge_root_rule_chain.json @@ -34,7 +34,11 @@ "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", "name": "Save Timeseries", "configuration": { - "defaultTTL": 0 + "defaultTTL": 0, + "useServerTs": false, + "persistenceSettings": { + "type": "ON_EVERY_MESSAGE" + } }, "externalId": null }, @@ -185,4 +189,4 @@ ], "ruleChainConnections": null } -} \ No newline at end of file +} diff --git a/application/src/main/data/json/tenant/device_profile/rule_chain_template.json b/application/src/main/data/json/tenant/device_profile/rule_chain_template.json index bce88d62b0..37cc226a6e 100644 --- a/application/src/main/data/json/tenant/device_profile/rule_chain_template.json +++ b/application/src/main/data/json/tenant/device_profile/rule_chain_template.json @@ -20,7 +20,11 @@ "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", "name": "Save Timeseries", "configuration": { - "defaultTTL": 0 + "defaultTTL": 0, + "useServerTs": false, + "persistenceSettings": { + "type": "ON_EVERY_MESSAGE" + } } }, { @@ -134,4 +138,4 @@ ], "ruleChainConnections": null } -} \ No newline at end of file +} diff --git a/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json b/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json index ee38849c1b..7893a64fd2 100644 --- a/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json +++ b/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json @@ -19,7 +19,11 @@ "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", "name": "Save Timeseries", "configuration": { - "defaultTTL": 0 + "defaultTTL": 0, + "useServerTs": false, + "persistenceSettings": { + "type": "ON_EVERY_MESSAGE" + } } }, { diff --git a/monitoring/src/main/resources/root_rule_chain.json b/monitoring/src/main/resources/root_rule_chain.json index ff44ebfe79..a48fd6bf35 100644 --- a/monitoring/src/main/resources/root_rule_chain.json +++ b/monitoring/src/main/resources/root_rule_chain.json @@ -23,7 +23,11 @@ "singletonMode": false, "configurationVersion": 0, "configuration": { - "defaultTTL": 0 + "defaultTTL": 0, + "useServerTs": false, + "persistenceSettings": { + "type": "ON_EVERY_MESSAGE" + } }, "externalId": null }, @@ -275,7 +279,11 @@ "singletonMode": false, "configurationVersion": 0, "configuration": { - "defaultTTL": 0 + "defaultTTL": 0, + "useServerTs": false, + "persistenceSettings": { + "type": "ON_EVERY_MESSAGE" + } }, "externalId": null }, @@ -310,8 +318,10 @@ "configurationVersion": 0, "configuration": { "defaultTTL": 180, - "skipLatestPersistence": null, - "useServerTs": null + "useServerTs": false, + "persistenceSettings": { + "type": "ON_EVERY_MESSAGE" + } }, "externalId": null } @@ -415,4 +425,4 @@ ], "ruleChainConnections": null } -} \ No newline at end of file +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java index b3eea21ece..1a014c5a43 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java @@ -83,6 +83,9 @@ public class TbMsgTimeseriesNode implements TbNode { ctx.addTenantProfileListener(this::onTenantProfileUpdate); onTenantProfileUpdate(ctx.getTenantProfile()); persistenceSettings = config.getPersistenceSettings(); + if (persistenceSettings == null) { + throw new TbNodeException("Persistence settings cannot be null!", true); + } } void onTenantProfileUpdate(TenantProfile tenantProfile) { From f8cfd158e279107e784014790accd2f689eb0b5a Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Sun, 12 Jan 2025 14:35:21 +0200 Subject: [PATCH 052/108] Save time series strategies: update one more time series node config in rule chain JSON --- .../src/test/resources/MqttRuleNodeTestMetadata.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/msa/black-box-tests/src/test/resources/MqttRuleNodeTestMetadata.json b/msa/black-box-tests/src/test/resources/MqttRuleNodeTestMetadata.json index c2bb52514e..adcb618cbe 100644 --- a/msa/black-box-tests/src/test/resources/MqttRuleNodeTestMetadata.json +++ b/msa/black-box-tests/src/test/resources/MqttRuleNodeTestMetadata.json @@ -39,8 +39,10 @@ "configurationVersion": 0, "configuration": { "defaultTTL": 0, - "skipLatestPersistence": false, - "useServerTs": false + "useServerTs": false, + "persistenceSettings": { + "type": "ON_EVERY_MESSAGE" + } }, "externalId": null }, From 698a0c19ecc61d654f61d0a52ea9ec7cdf68314a Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Mon, 13 Jan 2025 12:16:12 +0200 Subject: [PATCH 053/108] Save time series strategies: add SQL upgrade script --- .../main/data/upgrade/basic/schema_update.sql | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/application/src/main/data/upgrade/basic/schema_update.sql b/application/src/main/data/upgrade/basic/schema_update.sql index 64bbbaca05..0ff57119f4 100644 --- a/application/src/main/data/upgrade/basic/schema_update.sql +++ b/application/src/main/data/upgrade/basic/schema_update.sql @@ -209,3 +209,74 @@ $$; UPDATE resource SET resource_sub_type = 'EXTENSION' WHERE resource_type = 'JS_MODULE' AND resource_sub_type IS NULL; -- UPDATE RESOURCE JS_MODULE SUB TYPE END + +-- UPDATE SAVE TIME SERIES NODES START + +DO $$ + BEGIN + -- Check if the rule_node table exists + IF EXISTS ( + SELECT 1 + FROM information_schema.tables + WHERE table_name = 'rule_node' + ) THEN + + -- CREATE JSON validation function + CREATE OR REPLACE FUNCTION is_valid_jsonb(input text) + RETURNS boolean + LANGUAGE plpgsql + AS $func$ + DECLARE + dummy JSONB; + BEGIN + dummy := input::jsonb; + RETURN true; + EXCEPTION + WHEN others THEN + RETURN false; + END; + $func$; + + UPDATE rule_node + SET configuration = CASE + -- Case 1: If configuration is NULL, invalid JSON, or not a JSON object - set default configuration + WHEN configuration IS NULL + OR NOT is_valid_jsonb(configuration) + OR jsonb_typeof(configuration::jsonb) <> 'object' + THEN jsonb_build_object( + 'defaultTTL', 0, + 'useServerTs', false, + 'persistenceSettings', jsonb_build_object('type', 'ON_EVERY_MESSAGE') + ) + -- Case 2: If a valid JSON object with persistenceSettings (rule node was already upgraded) - leave unchanged + WHEN configuration::jsonb ? 'persistenceSettings' + THEN configuration::jsonb + -- Case 3: If a valid JSON object without persistenceSettings and skipLatestPersistence = 'true' (string 'true' or boolean true) - set latest to SKIP + WHEN configuration::jsonb ->> 'skipLatestPersistence' = 'true' + THEN (configuration::jsonb - 'skipLatestPersistence') + || jsonb_build_object( + 'persistenceSettings', jsonb_build_object( + 'type', 'ADVANCED', + 'timeseries', jsonb_build_object('type', 'ON_EVERY_MESSAGE'), + 'latest', jsonb_build_object('type', 'SKIP'), + 'webSockets', jsonb_build_object('type', 'ON_EVERY_MESSAGE') + ) + ) + -- Case 4: If a valid JSON object without persistenceSettings and skipLatestPersistence not 'true' (everything else) - set all to ON_EVERY_MESSAGE + ELSE (configuration::jsonb - 'skipLatestPersistence') + || jsonb_build_object( + 'persistenceSettings', jsonb_build_object( + 'type', 'ON_EVERY_MESSAGE' + ) + ) + END::text + WHERE type = 'org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode'; + + -- Drop the helper function + DROP FUNCTION is_valid_jsonb(text); + + END IF; + END; +$$; + +-- UPDATE SAVE TIME SERIES NODES END From 631d314fcebe4f63c36597ead46549596bc1d2a4 Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Mon, 13 Jan 2025 15:54:11 +0200 Subject: [PATCH 054/108] Save time series strategies: add Java upgrade script --- .../main/data/upgrade/basic/schema_update.sql | 5 +- .../engine/telemetry/TbMsgTimeseriesNode.java | 36 +++++++- .../telemetry/TbMsgTimeseriesNodeTest.java | 86 ++++++++++++++++++- 3 files changed, 121 insertions(+), 6 deletions(-) diff --git a/application/src/main/data/upgrade/basic/schema_update.sql b/application/src/main/data/upgrade/basic/schema_update.sql index 0ff57119f4..6aaf8c3dbd 100644 --- a/application/src/main/data/upgrade/basic/schema_update.sql +++ b/application/src/main/data/upgrade/basic/schema_update.sql @@ -269,8 +269,9 @@ DO $$ 'type', 'ON_EVERY_MESSAGE' ) ) - END::text - WHERE type = 'org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode'; + END::text, + configuration_version = 1 + WHERE type = 'org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode' AND configuration_version = 0; -- Drop the helper function DROP FUNCTION is_valid_jsonb(text); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java index 1a014c5a43..db2421e307 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java @@ -15,8 +15,11 @@ */ package org.thingsboard.rule.engine.telemetry; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.gson.JsonParser; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; @@ -24,6 +27,7 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.rule.engine.telemetry.strategy.PersistenceStrategy; import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TenantProfile; @@ -32,6 +36,7 @@ import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; +import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.common.msg.TbMsg; import java.util.ArrayList; @@ -66,7 +71,8 @@ import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_RE "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" + icon = "file_upload", + version = 1 ) public class TbMsgTimeseriesNode implements TbNode { @@ -182,4 +188,32 @@ public class TbMsgTimeseriesNode implements TbNode { ctx.removeListeners(); } + @Override + public TbPair upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException { + boolean hasChanges = false; + switch (fromVersion) { + case 0: + if (oldConfiguration.has("persistenceSettings")) { + break; + } + hasChanges = true; + JsonNode skipLatestPersistence = oldConfiguration.get("skipLatestPersistence"); + if (skipLatestPersistence != null && "true".equals(skipLatestPersistence.asText())) { + var skipLatestPersistenceSettings = new Advanced( + PersistenceStrategy.onEveryMessage(), + PersistenceStrategy.skip(), + PersistenceStrategy.onEveryMessage() + ); + ((ObjectNode) oldConfiguration).set("persistenceSettings", JacksonUtil.valueToTree(skipLatestPersistenceSettings)); + } else { + ((ObjectNode) oldConfiguration).set("persistenceSettings", JacksonUtil.valueToTree(new OnEveryMessage())); + } + ((ObjectNode) oldConfiguration).remove("skipLatestPersistence"); + break; + default: + break; + } + return new TbPair<>(hasChanges, oldConfiguration); + } + } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java index ba8c614944..71d060aab2 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java @@ -25,11 +25,12 @@ import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.MethodSource; import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.ThrowingConsumer; import org.mockito.junit.jupiter.MockitoExtension; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.AbstractRuleNodeUpgradeTest; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; @@ -60,12 +61,13 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.assertArg; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) -public class TbMsgTimeseriesNodeTest { +public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { private final TenantId TENANT_ID = TenantId.fromUUID(UUID.fromString("c8f34868-603a-4433-876a-7d356e5cf377")); private final DeviceId DEVICE_ID = new DeviceId(UUID.fromString("e5095e9a-04f4-44c9-b443-1cf1b97d3384")); @@ -82,7 +84,7 @@ public class TbMsgTimeseriesNodeTest { @BeforeEach public void setUp() throws TbNodeException { - node = new TbMsgTimeseriesNode(); + node = spy(new TbMsgTimeseriesNode()); config = new TbMsgTimeseriesNodeConfiguration().defaultConfiguration(); } @@ -291,4 +293,82 @@ public class TbMsgTimeseriesNodeTest { return expectedList; } + @Override + protected TbNode getTestNode() { + return node; + } + + private static Stream givenFromVersionAndConfig_whenUpgrade_thenVerifyHasChangesAndConfig() { + return Stream.of( + Arguments.of(0, """ + { + "defaultTTL": 0, + "useServerTs": false, + "skipLatestPersistence": false + }""", + true, + """ + { + "defaultTTL": 0, + "useServerTs": false, + "persistenceSettings": { + "type": "ON_EVERY_MESSAGE" + } + }"""), + Arguments.of(0, """ + { + "defaultTTL": 0, + "useServerTs": false + }""", + true, + """ + { + "defaultTTL": 0, + "useServerTs": false, + "persistenceSettings": { + "type": "ON_EVERY_MESSAGE" + } + }"""), + Arguments.of(0, """ + { + "defaultTTL": 0, + "useServerTs": false, + "skipLatestPersistence": null + }""", + true, + """ + { + "defaultTTL": 0, + "useServerTs": false, + "persistenceSettings": { + "type": "ON_EVERY_MESSAGE" + } + }"""), + Arguments.of(0, """ + { + "defaultTTL": 0, + "useServerTs": false, + "skipLatestPersistence": true + }""", + true, + """ + { + "defaultTTL": 0, + "useServerTs": false, + "persistenceSettings": { + "type": "ADVANCED", + "timeseries": { + "type": "ON_EVERY_MESSAGE" + }, + "latest": { + "type": "SKIP" + }, + "webSockets": { + "type": "ON_EVERY_MESSAGE" + } + } + }""") + ); + } + } From 0699de94845b56b9cc03c4dda319fa43b8d01dff Mon Sep 17 00:00:00 2001 From: mpetrov Date: Mon, 13 Jan 2025 18:26:35 +0200 Subject: [PATCH 055/108] Added custom translation support --- .../home/components/entity/entity-chips.component.html | 2 +- .../home/components/relation/relation-table.component.html | 4 ++-- .../components/widget/lib/home-page/doc-link.component.html | 2 +- .../widget/lib/home-page/doc-links-widget.component.html | 2 +- .../shared/components/dialog/confirm-dialog.component.html | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/entity/entity-chips.component.html b/ui-ngx/src/app/modules/home/components/entity/entity-chips.component.html index c81857689f..c162540b0c 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entity-chips.component.html +++ b/ui-ngx/src/app/modules/home/components/entity/entity-chips.component.html @@ -18,5 +18,5 @@ - {{ subEntity.name }} + {{ subEntity.name | customTranslate }} diff --git a/ui-ngx/src/app/modules/home/components/relation/relation-table.component.html b/ui-ngx/src/app/modules/home/components/relation/relation-table.component.html index fb49f299e9..4b0d4307f7 100644 --- a/ui-ngx/src/app/modules/home/components/relation/relation-table.component.html +++ b/ui-ngx/src/app/modules/home/components/relation/relation-table.component.html @@ -118,7 +118,7 @@ {{ 'relation.to-entity-name' | translate }} - {{ relation.toName }} + {{ relation.toName | customTranslate }} @@ -132,7 +132,7 @@ {{ 'relation.from-entity-name' | translate }} - {{ relation.fromName }} + {{ relation.fromName | customTranslate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-link.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-link.component.html index 4db770a919..dccfec03c2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-link.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-link.component.html @@ -49,7 +49,7 @@ - +
    diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-links-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-links-widget.component.html index 474824cb12..b1cb50d51b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-links-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-links-widget.component.html @@ -34,7 +34,7 @@ - +
    diff --git a/ui-ngx/src/app/shared/components/dialog/confirm-dialog.component.html b/ui-ngx/src/app/shared/components/dialog/confirm-dialog.component.html index c9bca14480..46d90f5ce0 100644 --- a/ui-ngx/src/app/shared/components/dialog/confirm-dialog.component.html +++ b/ui-ngx/src/app/shared/components/dialog/confirm-dialog.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -

    {{data.title}}

    +

    {{data.title | customTranslate}}

    From b8a8db3b3bc5479b56b1f0139fa359c51be46e45 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 14 Jan 2025 10:48:07 +0200 Subject: [PATCH 056/108] UI: Updated rule node config translate keys --- .../assign-customer-config.component.html | 8 +- .../action/attributes-config.component.html | 24 +- .../action/clear-alarm-config.component.html | 6 +- .../action/clear-alarm-config.component.ts | 4 +- .../action/create-alarm-config.component.html | 34 +- .../action/create-alarm-config.component.ts | 4 +- .../create-relation-config.component.html | 22 +- .../create-relation-config.component.ts | 18 +- .../delete-attributes-config.component.html | 24 +- .../delete-relation-config.component.html | 8 +- .../delete-relation-config.component.ts | 18 +- .../device-profile-config.component.html | 10 +- .../action/device-state-config.component.html | 2 +- .../action/generator-config.component.html | 26 +- .../action/generator-config.component.ts | 8 +- .../gps-geo-action-config.component.html | 76 +- .../rule-node/action/log-config.component.ts | 4 +- .../math-function-config.component.html | 32 +- .../action/msg-count-config.component.html | 10 +- .../action/msg-delay-config.component.html | 24 +- .../push-to-cloud-config.component.html | 8 +- .../action/push-to-edge-config.component.html | 8 +- .../action/rpc-reply-config.component.html | 10 +- .../action/rpc-request-config.component.html | 6 +- ...save-to-custom-table-config.component.html | 28 +- ...-rest-api-call-reply-config.component.html | 8 +- .../action/timeseries-config.component.html | 16 +- .../unassign-customer-config.component.html | 10 +- .../arguments-map-config.component.html | 24 +- .../common/credentials-config.component.html | 36 +- ...vice-relations-query-config.component.html | 20 +- .../common/example-hint.component.html | 2 +- .../common/kv-map-config-old.component.html | 6 +- .../common/kv-map-config.component.html | 8 +- .../math-function-autocomplete.component.html | 4 +- .../message-types-config.component.html | 10 +- .../common/message-types-config.component.ts | 2 +- ...t-message-type-autocomplete.component.html | 10 +- .../relations-query-config-old.component.html | 4 +- .../relations-query-config.component.html | 14 +- .../common/select-attributes.component.html | 24 +- .../common/sv-map-config.component.html | 6 +- .../calculate-delta-config.component.html | 44 +- .../customer-attributes-config.component.html | 18 +- .../device-attributes-config.component.html | 12 +- .../entity-details-config.component.html | 10 +- ...h-device-credentials-config.component.html | 2 +- ...emetry-from-database-config.component.html | 66 +- ...riginator-attributes-config.component.html | 10 +- .../originator-fields-config.component.html | 20 +- .../related-attributes-config.component.html | 30 +- .../tenant-attributes-config.component.html | 18 +- .../azure-iot-hub-config.component.html | 50 +- .../external/kafka-config.component.html | 58 +- .../external/lambda-config.component.html | 48 +- .../external/mqtt-config.component.html | 44 +- .../external/pubsub-config.component.html | 26 +- .../external/rabbit-mq-config.component.html | 44 +- .../rest-api-call-config.component.html | 66 +- .../external/send-email-config.component.html | 50 +- .../external/send-sms-config.component.html | 14 +- .../external/slack-config.component.html | 12 +- .../external/sns-config.component.html | 18 +- .../external/sqs-config.component.html | 38 +- .../filter/check-alarm-status.component.html | 4 +- .../check-message-config.component.html | 20 +- .../check-relation-config.component.html | 8 +- .../gps-geo-filter-config.component.html | 50 +- .../filter/message-type-config.component.html | 2 +- .../originator-type-config.component.html | 8 +- .../filter/script-config.component.ts | 4 +- .../filter/switch-config.component.ts | 4 +- .../flow/rule-chain-input.component.html | 4 +- .../flow/rule-chain-output.component.html | 2 +- .../rule-node/rule-node-config.models.ts | 188 +-- .../change-originator-config.component.html | 8 +- .../copy-keys-config.component.html | 10 +- .../deduplication-config.component.html | 38 +- .../delete-keys-config.component.html | 10 +- .../node-json-path-config.component.html | 6 +- .../rename-keys-config.component.html | 14 +- .../transformation/script-config.component.ts | 4 +- .../to-email-config.component.html | 40 +- .../to-email-config.component.ts | 12 +- .../assets/locale/locale.constant-en_US.json | 1463 +++++++++-------- 85 files changed, 1615 insertions(+), 1608 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/assign-customer-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/assign-customer-config.component.html index 486764f936..4ec27d83e7 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/assign-customer-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/assign-customer-config.component.html @@ -18,17 +18,17 @@
    - tb.rulenode.customer-name-pattern + rule-node-config.customer-name-pattern - {{ 'tb.rulenode.customer-name-pattern-required' | translate }} + {{ 'rule-node-config.customer-name-pattern-required' | translate }} - tb.rulenode.customer-name-pattern-hint + rule-node-config.customer-name-pattern-hint
    - {{ 'tb.rulenode.create-customer-if-not-exists' | translate }} + {{ 'rule-node-config.create-customer-if-not-exists' | translate }}
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/attributes-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/attributes-config.component.html index 80633ca0d3..258f856e91 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/attributes-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/attributes-config.component.html @@ -17,11 +17,11 @@ -->
    - +
    - {{ 'tb.rulenode.attributes-scope' | translate }} + {{ 'rule-node-config.attributes-scope' | translate }} @@ -30,7 +30,7 @@ - {{ 'tb.rulenode.attributes-scope-value' | translate }} + {{ 'rule-node-config.attributes-scope-value' | translate }}
    - tb.rulenode.alarm-type + rule-node-config.alarm-type - {{ 'tb.rulenode.alarm-type-required' | translate }} + {{ 'rule-node-config.alarm-type-required' | translate }} - tb.rulenode.general-pattern-hint + rule-node-config.general-pattern-hint
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/clear-alarm-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/clear-alarm-config.component.ts index f132d737eb..cf96982621 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/clear-alarm-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/clear-alarm-config.component.ts @@ -47,7 +47,7 @@ export class ClearAlarmConfigComponent extends RuleNodeConfigurationComponent { readonly hasScript = true; - readonly testScriptLabel = 'tb.rulenode.test-details-function'; + readonly testScriptLabel = 'rule-node-config.test-details-function'; constructor(protected store: Store, private fb: UntypedFormBuilder, @@ -103,7 +103,7 @@ export class ClearAlarmConfigComponent extends RuleNodeConfigurationComponent { this.nodeScriptTestService.testNodeScript( script, 'json', - this.translate.instant('tb.rulenode.details'), + this.translate.instant('rule-node-config.details'), 'Details', ['msg', 'metadata', 'msgType'], this.ruleNodeId, diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/create-alarm-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/create-alarm-config.component.html index 9b1efd0f1a..9ec4238081 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/create-alarm-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/create-alarm-config.component.html @@ -17,10 +17,10 @@ -->
    - {{ 'tb.rulenode.use-message-alarm-data' | translate }} + {{ 'rule-node-config.use-message-alarm-data' | translate }} - {{ 'tb.rulenode.overwrite-alarm-details' | translate }} + {{ 'rule-node-config.overwrite-alarm-details' | translate }}
    @@ -67,41 +67,41 @@
    - tb.rulenode.alarm-type + rule-node-config.alarm-type - {{ 'tb.rulenode.alarm-type-required' | translate }} + {{ 'rule-node-config.alarm-type-required' | translate }} - tb.rulenode.general-pattern-hint + rule-node-config.general-pattern-hint - {{ 'tb.rulenode.use-alarm-severity-pattern' | translate }} + {{ 'rule-node-config.use-alarm-severity-pattern' | translate }} - tb.rulenode.alarm-severity + rule-node-config.alarm-severity {{ alarmSeverityTranslationMap.get(severity) | translate }} - {{ 'tb.rulenode.alarm-severity-required' | translate }} + {{ 'rule-node-config.alarm-severity-required' | translate }} - tb.rulenode.alarm-severity-pattern + rule-node-config.alarm-severity-pattern - {{ 'tb.rulenode.alarm-severity-required' | translate }} + {{ 'rule-node-config.alarm-severity-required' | translate }} - + - {{ 'tb.rulenode.propagate' | translate }} + {{ 'rule-node-config.propagate' | translate }}
    - tb.rulenode.relation-types-list + rule-node-config.relation-types-list close - - tb.rulenode.relation-types-list-hint + rule-node-config.relation-types-list-hint
    - {{ 'tb.rulenode.propagate-to-owner' | translate }} + {{ 'rule-node-config.propagate-to-owner' | translate }} - {{ 'tb.rulenode.propagate-to-tenant' | translate }} + {{ 'rule-node-config.propagate-to-tenant' | translate }}
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/create-alarm-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/create-alarm-config.component.ts index b5639f15ac..a68eedfbfc 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/create-alarm-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/create-alarm-config.component.ts @@ -54,7 +54,7 @@ export class CreateAlarmConfigComponent extends RuleNodeConfigurationComponent { readonly hasScript = true; - readonly testScriptLabel = 'tb.rulenode.test-details-function'; + readonly testScriptLabel = 'rule-node-config.test-details-function'; constructor(protected store: Store, private fb: UntypedFormBuilder, @@ -143,7 +143,7 @@ export class CreateAlarmConfigComponent extends RuleNodeConfigurationComponent { this.nodeScriptTestService.testNodeScript( script, 'json', - this.translate.instant('tb.rulenode.details'), + this.translate.instant('rule-node-config.details'), 'Details', ['msg', 'metadata', 'msgType'], this.ruleNodeId, diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/create-relation-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/create-relation-config.component.html index abbdb99f53..362bb4d9b6 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/create-relation-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/create-relation-config.component.html @@ -17,7 +17,7 @@ -->
    -
    tb.rulenode.relation-parameters
    +
    rule-node-config.relation-parameters
    relation.direction @@ -35,7 +35,7 @@
    -
    tb.rulenode.target-entity
    +
    rule-node-config.target-entity
    - tb.rulenode.profile-name + rule-node-config.profile-name
    - -
    - {{ 'tb.rulenode.create-entity-if-not-exists' | translate }} + {{ 'rule-node-config.create-entity-if-not-exists' | translate }}
    - tb.rulenode.advanced-settings + rule-node-config.advanced-settings
    -
    - {{ 'tb.rulenode.remove-current-relations' | translate }} + {{ 'rule-node-config.remove-current-relations' | translate }}
    -
    - {{ 'tb.rulenode.change-originator-to-related-entity' | translate }} + {{ 'rule-node-config.change-originator-to-related-entity' | translate }}
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/create-relation-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/create-relation-config.component.ts index 53c8da3bd6..b48a530b57 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/create-relation-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/create-relation-config.component.ts @@ -30,8 +30,8 @@ export class CreateRelationConfigComponent extends RuleNodeConfigurationComponen directionTypes = Object.keys(EntitySearchDirection); directionTypeTranslations = new Map( [ - [EntitySearchDirection.FROM, 'tb.rulenode.search-direction-from'], - [EntitySearchDirection.TO, 'tb.rulenode.search-direction-to'], + [EntitySearchDirection.FROM, 'rule-node-config.search-direction-from'], + [EntitySearchDirection.TO, 'rule-node-config.search-direction-to'], ] ); @@ -39,13 +39,13 @@ export class CreateRelationConfigComponent extends RuleNodeConfigurationComponen entityTypeNamePatternTranslation = new Map( [ - [EntityType.DEVICE, 'tb.rulenode.device-name-pattern'], - [EntityType.ASSET, 'tb.rulenode.asset-name-pattern'], - [EntityType.ENTITY_VIEW, 'tb.rulenode.entity-view-name-pattern'], - [EntityType.CUSTOMER, 'tb.rulenode.customer-title-pattern'], - [EntityType.USER, 'tb.rulenode.user-name-pattern'], - [EntityType.DASHBOARD, 'tb.rulenode.dashboard-name-pattern'], - [EntityType.EDGE, 'tb.rulenode.edge-name-pattern'] + [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'] ] ); diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/delete-attributes-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/delete-attributes-config.component.html index 7050cae88a..0f62bec757 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/delete-attributes-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/delete-attributes-config.component.html @@ -17,11 +17,11 @@ -->
    - +
    - {{ 'tb.rulenode.attributes-scope' | translate }} + {{ 'rule-node-config.attributes-scope' | translate }} @@ -30,7 +30,7 @@ - {{ 'tb.rulenode.attributes-scope-value' | translate }} + {{ 'rule-node-config.attributes-scope-value' | translate }}
    - {{ 'tb.rulenode.attributes-keys' | translate }} + {{ 'rule-node-config.attributes-keys' | translate }} - {{ 'tb.rulenode.attributes-keys-required' | translate }} - tb.rulenode.general-pattern-hint + {{ 'rule-node-config.attributes-keys-required' | translate }} + rule-node-config.general-pattern-hint
    - tb.rulenode.advanced-settings + rule-node-config.advanced-settings -
    - {{ 'tb.rulenode.send-attributes-deleted-notification' | translate }} + {{ 'rule-node-config.send-attributes-deleted-notification' | translate }}
    -
    - {{ 'tb.rulenode.notify-device' | translate }} + {{ 'rule-node-config.notify-device' | translate }}
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/delete-relation-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/delete-relation-config.component.html index bfd091dc57..3270f95a53 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/delete-relation-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/delete-relation-config.component.html @@ -17,7 +17,7 @@ -->
    -
    tb.rulenode.relation-parameters
    +
    rule-node-config.relation-parameters
    relation.direction @@ -34,10 +34,10 @@
    -
    - {{ 'tb.rulenode.delete-relation-with-specific-entity' | translate }} + {{ 'rule-node-config.delete-relation-with-specific-entity' | translate }}
    @@ -56,7 +56,7 @@
    + [hintText]="'rule-node-config.kv-map-single-pattern-hint'">
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/delete-relation-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/delete-relation-config.component.ts index a59793f767..34337f95ad 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/delete-relation-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/delete-relation-config.component.ts @@ -31,20 +31,20 @@ export class DeleteRelationConfigComponent extends RuleNodeConfigurationComponen directionTypeTranslations = new Map( [ - [EntitySearchDirection.FROM, 'tb.rulenode.del-relation-direction-from'], - [EntitySearchDirection.TO, 'tb.rulenode.del-relation-direction-to'], + [EntitySearchDirection.FROM, 'rule-node-config.del-relation-direction-from'], + [EntitySearchDirection.TO, 'rule-node-config.del-relation-direction-to'], ] ); entityTypeNamePatternTranslation = new Map( [ - [EntityType.DEVICE, 'tb.rulenode.device-name-pattern'], - [EntityType.ASSET, 'tb.rulenode.asset-name-pattern'], - [EntityType.ENTITY_VIEW, 'tb.rulenode.entity-view-name-pattern'], - [EntityType.CUSTOMER, 'tb.rulenode.customer-title-pattern'], - [EntityType.USER, 'tb.rulenode.user-name-pattern'], - [EntityType.DASHBOARD, 'tb.rulenode.dashboard-name-pattern'], - [EntityType.EDGE, 'tb.rulenode.edge-name-pattern'] + [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'] ] ); diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/device-profile-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/device-profile-config.component.html index bb897c6a2d..74594f1400 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/device-profile-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/device-profile-config.component.html @@ -16,17 +16,17 @@ -->
    -
    tb.rulenode.device-profile-node-hint
    -
    rule-node-config.device-profile-node-hint
    +
    - {{ 'tb.rulenode.persist-alarm-rules' | translate }} + {{ 'rule-node-config.persist-alarm-rules' | translate }}
    -
    - {{ 'tb.rulenode.fetch-alarm-rules' | translate }} + {{ 'rule-node-config.fetch-alarm-rules' | translate }}
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/device-state-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/device-state-config.component.html index cae70173ff..812c5e9aed 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/device-state-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/device-state-config.component.html @@ -17,7 +17,7 @@ -->
    - {{ 'tb.rulenode.select-device-connectivity-event' | translate }} + {{ 'rule-node-config.select-device-connectivity-event' | translate }} {{ messageTypeNames.get(eventOption) }} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/generator-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/generator-config.component.html index a3ef3821bd..cdddbf60da 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/generator-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/generator-config.component.html @@ -17,33 +17,33 @@ -->
    -
    tb.rulenode.generation-parameters
    +
    rule-node-config.generation-parameters
    - tb.rulenode.message-count + rule-node-config.message-count - {{ 'tb.rulenode.message-count-required' | translate }} + {{ 'rule-node-config.message-count-required' | translate }} - {{ 'tb.rulenode.min-message-count-message' | translate }} + {{ 'rule-node-config.min-message-count-message' | translate }} - tb.rulenode.generation-frequency-seconds + rule-node-config.generation-frequency-seconds - {{ 'tb.rulenode.generation-frequency-required' | translate }} + {{ 'rule-node-config.generation-frequency-required' | translate }} - {{ 'tb.rulenode.min-generation-frequency-message' | translate }} + {{ 'rule-node-config.min-generation-frequency-message' | translate }}
    -
    tb.rulenode.originator
    +
    rule-node-config.originator
    - tb.rulenode.generator-function + rule-node-config.generator-function - {{ 'tb.rulenode.script-lang-tbel' | translate }} + {{ 'rule-node-config.script-lang-tbel' | translate }} - {{ 'tb.rulenode.script-lang-js' | translate }} + {{ 'rule-node-config.script-lang-js' | translate }}
    tb.rulenode.no-arguments-prompt + class="tb-prompt flex items-center justify-center">rule-node-config.no-arguments-prompt
    -
    @@ -55,12 +55,12 @@ help + matTooltip="{{ 'rule-node-config.chip-help' | translate: { inputName: 'rule-node-config.device-profile' | translate } }}">help
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/example-hint.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/example-hint.component.html index b10c9f16b8..238fdce6af 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/example-hint.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/example-hint.component.html @@ -24,6 +24,6 @@ tb-help-popup-placement="right" trigger-style="letter-spacing:0.25px; font-size:12px" [tb-help-popup-style]="{maxWidth: '820px'}" - trigger-text="{{ 'tb.key-val.see-examples' | translate }}"> + trigger-text="{{ 'rule-node-config.key-val.see-examples' | translate }}">
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config-old.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config-old.component.html index 761b9c9491..6678008fd0 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config-old.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config-old.component.html @@ -44,7 +44,7 @@ type="button" (click)="removeKeyVal($index)" [disabled]="isLoading$ | async" - matTooltip="{{ 'tb.key-val.remove-entry' | translate }}" + matTooltip="{{ 'rule-node-config.key-val.remove-entry' | translate }}" matTooltipPosition="above"> close @@ -53,7 +53,7 @@
    - tb.rulenode.map-fields-required + rule-node-config.map-fields-required
    - {{ 'tb.key-val.unique-key-value-pair-error' | translate: + {{ 'rule-node-config.key-val.unique-key-value-pair-error' | translate: { valText: valText, keyText: keyText @@ -54,7 +54,7 @@ mat-icon-button (click)="removeKeyVal($index)" [disabled]="disabled" - matTooltip="{{ 'tb.key-val.remove-mapping-entry' | translate }}" + matTooltip="{{ 'rule-node-config.key-val.remove-mapping-entry' | translate }}" matTooltipPosition="above"> delete @@ -65,7 +65,7 @@
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/math-function-autocomplete.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/math-function-autocomplete.component.html index 6a2bf514ad..eb122fe028 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/math-function-autocomplete.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/math-function-autocomplete.component.html @@ -16,7 +16,7 @@ --> - tb.rulenode.functions-field-input + rule-node-config.functions-field-input - tb.rulenode.no-option-found + rule-node-config.no-option-found diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/message-types-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/message-types-config.component.html index df52912735..174933847e 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/message-types-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/message-types-config.component.html @@ -47,25 +47,25 @@
    - tb.rulenode.no-message-types-found + rule-node-config.no-message-types-found
    - {{ 'tb.rulenode.no-message-type-matching' | translate : + {{ 'rule-node-config.no-message-type-matching' | translate : {messageType: truncate.transform(searchText, true, 6, '...')} }} - tb.rulenode.create-new-message-type + rule-node-config.create-new-message-type
    help + matTooltip="{{ 'rule-node-config.chip-help' | translate: { inputName: 'rule-node-config.message-type' | translate } }}">help - {{ 'tb.rulenode.select-message-types-required' | translate }} + {{ 'rule-node-config.select-message-types-required' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/message-types-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/message-types-config.component.ts index 278819cb80..686c6150aa 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/message-types-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/message-types-config.component.ts @@ -56,7 +56,7 @@ export class MessageTypesConfigComponent extends PageComponent implements Contro label: string; @Input() - placeholder = 'tb.rulenode.add-message-type'; + placeholder = 'rule-node-config.add-message-type'; @Input() disabled: boolean; diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/output-message-type-autocomplete.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/output-message-type-autocomplete.component.html index 832c633d5d..83e2547286 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/output-message-type-autocomplete.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/output-message-type-autocomplete.component.html @@ -17,7 +17,7 @@ -->
    - {{'tb.rulenode.output-message-type' | translate}} + {{'rule-node-config.output-message-type' | translate}} {{msgType.name}} @@ -25,7 +25,7 @@ - {{'tb.rulenode.message-type-value' | translate}} + {{'rule-node-config.message-type-value' | translate}} - {{ 'tb.rulenode.message-type-value-required' | translate }} + {{ 'rule-node-config.message-type-value-required' | translate }} - {{ 'tb.rulenode.message-type-value-max-length' | translate }} + {{ 'rule-node-config.message-type-value-max-length' | translate }}
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config-old.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config-old.component.html index d60e993e88..6fb7a86005 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config-old.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config-old.component.html @@ -29,12 +29,12 @@ - tb.rulenode.max-relation-level + rule-node-config.max-relation-level
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config.component.html index 748076957d..01afac76b6 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config.component.html @@ -16,35 +16,35 @@ -->
    -
    tb.rulenode.relations-query
    +
    rule-node-config.relations-query
    relation.direction - {{ directionTypeTranslations.get(type) | translate }} tb.rulenode.relations-query-config-direction-suffix + {{ directionTypeTranslations.get(type) | translate }} rule-node-config.relations-query-config-direction-suffix - tb.rulenode.max-relation-level + rule-node-config.max-relation-level - {{ 'tb.rulenode.max-relation-level-error' | translate }} + {{ 'rule-node-config.max-relation-level-error' | translate }} - {{ 'tb.rulenode.max-relation-level-invalid' | translate }} + {{ 'rule-node-config.max-relation-level-invalid' | translate }}
    -
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/select-attributes.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/select-attributes.component.html index 06d63535e9..08de169272 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/select-attributes.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/select-attributes.component.html @@ -16,38 +16,38 @@ -->
    - + [placeholder]="'rule-node-config.add-attribute-key' | translate" + [label]="'rule-node-config.client-attributes' | translate" formControlName="clientAttributeNames"> + [placeholder]="'rule-node-config.add-attribute-key' | translate" + [label]="'rule-node-config.shared-attributes' | translate" formControlName="sharedAttributeNames"> + [placeholder]="'rule-node-config.add-attribute-key' | translate" + [label]="'rule-node-config.server-attributes' | translate" formControlName="serverAttributeNames"> + [placeholder]="'rule-node-config.add-telemetry-key' | translate" + [label]="'rule-node-config.latest-telemetry' | translate" formControlName="latestTsKeyNames"> -
    - {{ 'tb.rulenode.fetch-latest-telemetry-with-timestamp' | translate }} + {{ 'rule-node-config.fetch-latest-telemetry-with-timestamp' | translate }}
    @@ -55,5 +55,5 @@ help + matTooltip="{{ 'rule-node-config.chip-help' | translate: { inputName: 'rule-node-config.field-name' | translate } }}">help diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/sv-map-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/sv-map-config.component.html index 6deeb54f67..21ed014ec3 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/sv-map-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/sv-map-config.component.html @@ -19,7 +19,7 @@
    {{ labelText }}
    - tb.rulenode.map-fields-required + rule-node-config.map-fields-required
    {{ requiredText }} @@ -51,7 +51,7 @@ mat-icon-button (click)="removeKeyVal($index)" [disabled]="isLoading$ | async" - matTooltip="{{ 'tb.key-val.remove-mapping-entry' | translate }}" + matTooltip="{{ 'rule-node-config.key-val.remove-mapping-entry' | translate }}" matTooltipPosition="above"> delete @@ -63,7 +63,7 @@
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/calculate-delta-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/calculate-delta-config.component.html index b727008bb3..6d9924f4f8 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/calculate-delta-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/calculate-delta-config.component.html @@ -18,74 +18,74 @@
    - {{ 'tb.rulenode.input-value-key' | translate }} + {{ 'rule-node-config.input-value-key' | translate }} - {{ 'tb.rulenode.input-value-key-required' | translate }} + {{ 'rule-node-config.input-value-key-required' | translate }} - {{ 'tb.rulenode.output-value-key' | translate }} + {{ 'rule-node-config.output-value-key' | translate }} - {{ 'tb.rulenode.output-value-key-required' | translate }} + {{ 'rule-node-config.output-value-key-required' | translate }}
    - {{ 'tb.rulenode.number-of-digits-after-floating-point' | translate }} + {{ 'rule-node-config.number-of-digits-after-floating-point' | translate }} - {{ 'tb.rulenode.number-of-digits-after-floating-point-range' | translate }} + {{ 'rule-node-config.number-of-digits-after-floating-point-range' | translate }} - {{ 'tb.rulenode.number-of-digits-after-floating-point-range' | translate }} + {{ 'rule-node-config.number-of-digits-after-floating-point-range' | translate }}
    -
    - {{ 'tb.rulenode.failure-if-delta-negative' | translate }} + {{ 'rule-node-config.failure-if-delta-negative' | translate }}
    -
    - {{ 'tb.rulenode.use-caching' | translate }} + {{ 'rule-node-config.use-caching' | translate }}
    -
    - {{ 'tb.rulenode.add-time-difference-between-readings' | translate: + {{ 'rule-node-config.add-time-difference-between-readings' | translate: { inputValueKey: calculateDeltaConfigForm.get('inputValueKey').valid ? - calculateDeltaConfigForm.get('inputValueKey').value : 'tb.rulenode.input-value-key' | translate } }} + calculateDeltaConfigForm.get('inputValueKey').value : 'rule-node-config.input-value-key' | translate } }}
    - {{ 'tb.rulenode.period-value-key' | translate }} + {{ 'rule-node-config.period-value-key' | translate }} - {{ 'tb.rulenode.period-value-key-required' | translate }} + {{ 'rule-node-config.period-value-key-required' | translate }}
    -
    - {{ 'tb.rulenode.exclude-zero-deltas' | translate }} + {{ 'rule-node-config.exclude-zero-deltas' | translate }}
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/customer-attributes-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/customer-attributes-config.component.html index 182d580eec..20377be84b 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/customer-attributes-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/customer-attributes-config.component.html @@ -16,7 +16,7 @@ -->
    -
    tb.rulenode.mapping-of-customers
    +
    rule-node-config.mapping-of-customers
    @@ -29,18 +29,18 @@
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/device-attributes-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/device-attributes-config.component.html index c17edb1e92..f2c33cf0b5 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/device-attributes-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/device-attributes-config.component.html @@ -17,7 +17,7 @@ -->
    -
    tb.rulenode.device-relations-query
    +
    rule-node-config.device-relations-query
    @@ -25,22 +25,22 @@
    -
    tb.rulenode.related-device-attributes
    +
    rule-node-config.related-device-attributes
    - tb.rulenode.at-least-one-field-required + rule-node-config.at-least-one-field-required
    + [labelText]="'rule-node-config.add-selected-attributes-to' | translate">
    -
    - {{ 'tb.rulenode.tell-failure' | translate }} + {{ 'rule-node-config.tell-failure' | translate }}
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/entity-details-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/entity-details-config.component.html index 1065aa6a09..07fc97a716 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/entity-details-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/entity-details-config.component.html @@ -18,18 +18,18 @@
    + matTooltip="{{ 'rule-node-config.chip-help' | translate: { inputName: 'rule-node-config.detail' | translate } }}"> help
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/fetch-device-credentials-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/fetch-device-credentials-config.component.html index a76d3341d7..aa210dfcc1 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/fetch-device-credentials-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/fetch-device-credentials-config.component.html @@ -17,7 +17,7 @@ -->
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/get-telemetry-from-database-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/get-telemetry-from-database-config.component.html index 9b656096f8..6dafe65457 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/get-telemetry-from-database-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/get-telemetry-from-database-config.component.html @@ -16,47 +16,47 @@ -->
    - + [requiredText]="'rule-node-config.timeseries-keys-required' | translate" + [label]="'rule-node-config.timeseries-keys' | translate" formControlName="latestTsKeyNames" + [hint]="'rule-node-config.general-pattern-hint' | translate">
    + trigger-text="{{ 'rule-node-config.key-val.see-examples' | translate }}">
    help + matTooltip="{{ 'rule-node-config.chip-help' | translate: { inputName: 'rule-node-config.timeseries-key' | translate } }}">help
    -
    tb.rulenode.fetch-interval
    -
    rule-node-config.fetch-interval
    +
    - {{ 'tb.rulenode.use-metadata-dynamic-interval' | translate }} + {{ 'rule-node-config.use-metadata-dynamic-interval' | translate }}
    - {{ 'tb.rulenode.interval-start' | translate }} + {{ 'rule-node-config.interval-start' | translate }} - {{ 'tb.rulenode.start-interval-value-required' | translate }} + {{ 'rule-node-config.start-interval-value-required' | translate }} - {{ 'tb.rulenode.time-value-range' | translate }} + {{ 'rule-node-config.time-value-range' | translate }} - {{ 'tb.rulenode.time-value-range' | translate }} + {{ 'rule-node-config.time-value-range' | translate }} - {{ 'tb.rulenode.time-unit' | translate }} + {{ 'rule-node-config.time-unit' | translate }} {{ timeUnitsTranslationMap.get(timeUnit) | translate }} @@ -66,20 +66,20 @@
    - {{ 'tb.rulenode.interval-end' | translate }} + {{ 'rule-node-config.interval-end' | translate }} - {{ 'tb.rulenode.end-interval-value-required' | translate }} + {{ 'rule-node-config.end-interval-value-required' | translate }} - {{ 'tb.rulenode.time-value-range' | translate }} + {{ 'rule-node-config.time-value-range' | translate }} - {{ 'tb.rulenode.time-value-range' | translate }} + {{ 'rule-node-config.time-value-range' | translate }} - {{ 'tb.rulenode.time-unit' | translate }} + {{ 'rule-node-config.time-unit' | translate }} {{ timeUnitsTranslationMap.get(timeUnit) | translate }} @@ -91,7 +91,7 @@ error_outline
    - {{ 'tb.rulenode.fetch-timeseries-from-to' | translate: + {{ 'rule-node-config.fetch-timeseries-from-to' | translate: { startInterval: getTelemetryFromDatabaseConfigForm.get('interval.startInterval').value, endInterval: getTelemetryFromDatabaseConfigForm.get('interval.endInterval').value, @@ -100,7 +100,7 @@ } }} - {{ "tb.rulenode.fetch-timeseries-from-to-invalid" | translate }} + {{ "rule-node-config.fetch-timeseries-from-to-invalid" | translate }}
    @@ -108,29 +108,29 @@
    - {{ 'tb.rulenode.start-interval' | translate }} + {{ 'rule-node-config.start-interval' | translate }} - {{ 'tb.rulenode.start-interval-required' | translate }} + {{ 'rule-node-config.start-interval-required' | translate }} - {{ 'tb.rulenode.end-interval' | translate }} + {{ 'rule-node-config.end-interval' | translate }} - {{ 'tb.rulenode.end-interval-required' | translate }} + {{ 'rule-node-config.end-interval-required' | translate }} -
    -
    tb.rulenode.fetch-strategy
    +
    rule-node-config.fetch-strategy
    @@ -155,7 +155,7 @@
    - {{ "tb.rulenode.order-by-timestamp" | translate }} + {{ "rule-node-config.order-by-timestamp" | translate }} {{ samplingOrdersTranslate.get(order) | translate }} @@ -163,17 +163,17 @@ - {{ "tb.rulenode.limit" | translate }} + {{ "rule-node-config.limit" | translate }} - {{ "tb.rulenode.limit-hint" | translate }} + {{ "rule-node-config.limit-hint" | translate }} - {{ 'tb.rulenode.limit-required' | translate }} + {{ 'rule-node-config.limit-required' | translate }} - {{ 'tb.rulenode.limit-range' | translate }} + {{ 'rule-node-config.limit-range' | translate }} - {{ 'tb.rulenode.limit-range' | translate }} + {{ 'rule-node-config.limit-range' | translate }}
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/originator-attributes-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/originator-attributes-config.component.html index caf7c63d11..6c767c3664 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/originator-attributes-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/originator-attributes-config.component.html @@ -18,23 +18,23 @@
    -
    tb.rulenode.originator-attributes
    +
    rule-node-config.originator-attributes
    - tb.rulenode.at-least-one-field-required + rule-node-config.at-least-one-field-required
    -
    -
    +
    - {{ 'tb.rulenode.tell-failure' | translate }} + {{ 'rule-node-config.tell-failure' | translate }}
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/originator-fields-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/originator-fields-config.component.html index 6ec066f230..4f5ed6cf13 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/originator-fields-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/originator-fields-config.component.html @@ -21,21 +21,21 @@ [selectOptions]="originatorFields" targetKeyPrefix="originator" formControlName="dataMapping" - [requiredText]="'tb.rulenode.attr-mapping-required' | translate" - [labelText]="'tb.rulenode.originator-fields-mapping' | translate" - [selectText]="'tb.rulenode.source-field' | translate" - [selectRequiredText]="'tb.rulenode.source-field-required' | translate" - [valText]="'tb.rulenode.target-key' | translate" - [valRequiredText]="'tb.rulenode.target-key-required' | translate" - [hintText]="'tb.rulenode.originator-fields-sv-map-hint' | translate" + [requiredText]="'rule-node-config.attr-mapping-required' | translate" + [labelText]="'rule-node-config.originator-fields-mapping' | translate" + [selectText]="'rule-node-config.source-field' | translate" + [selectRequiredText]="'rule-node-config.source-field-required' | translate" + [valText]="'rule-node-config.target-key' | translate" + [valRequiredText]="'rule-node-config.target-key-required' | translate" + [hintText]="'rule-node-config.originator-fields-sv-map-hint' | translate" popupHelpLink="rulenode/originator_fields_node_fields_templatization"> - -
    +
    - {{ 'tb.rulenode.skip-empty-fields' | translate }} + {{ 'rule-node-config.skip-empty-fields' | translate }}
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/related-attributes-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/related-attributes-config.component.html index a7d9f74e1d..677ffb52eb 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/related-attributes-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/related-attributes-config.component.html @@ -21,7 +21,7 @@ formControlName="relationsQuery">
    -
    tb.rulenode.data-to-fetch
    +
    rule-node-config.data-to-fetch
    {{ data.name }} @@ -31,28 +31,28 @@ [hidden]="relatedAttributesConfigForm.get('dataToFetch').value === DataToFetch.FIELDS" required formControlName="kvMap" - [requiredText]="'tb.rulenode.attr-mapping-required' | translate" - [labelText]="selectTranslation('tb.rulenode.latest-telemetry-mapping','tb.rulenode.attributes-mapping') | translate" - [keyText]="selectTranslation('tb.rulenode.source-telemetry','tb.rulenode.source-attribute') | translate" - [keyRequiredText]="selectTranslation('tb.rulenode.source-telemetry-required','tb.rulenode.source-attribute-required') | translate" - [valText]="'tb.rulenode.target-key' | translate" - [valRequiredText]="'tb.rulenode.target-key-required' | translate" - [hintText]="'tb.rulenode.kv-map-pattern-hint'" + [requiredText]="'rule-node-config.attr-mapping-required' | translate" + [labelText]="selectTranslation('rule-node-config.latest-telemetry-mapping','rule-node-config.attributes-mapping') | translate" + [keyText]="selectTranslation('rule-node-config.source-telemetry','rule-node-config.source-attribute') | translate" + [keyRequiredText]="selectTranslation('rule-node-config.source-telemetry-required','rule-node-config.source-attribute-required') | translate" + [valText]="'rule-node-config.target-key' | translate" + [valRequiredText]="'rule-node-config.target-key-required' | translate" + [hintText]="'rule-node-config.kv-map-pattern-hint'" popupHelpLink="rulenode/related_entity_data_node_fields_templatization">
    -
    tb.rulenode.mapping-of-tenant
    +
    rule-node-config.mapping-of-tenant
    @@ -29,17 +29,17 @@ + ('rule-node-config.add-mapped-latest-telemetry-to' | translate) : ('rule-node-config.add-mapped-attribute-to' | translate)">
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/azure-iot-hub-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/azure-iot-hub-config.component.html index 44fe7d2ebd..59dcb40fec 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/external/azure-iot-hub-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/azure-iot-hub-config.component.html @@ -17,45 +17,45 @@ -->
    - tb.rulenode.topic + rule-node-config.topic - {{ 'tb.rulenode.topic-required' | translate }} + {{ 'rule-node-config.topic-required' | translate }} - tb.rulenode.general-pattern-hint + rule-node-config.general-pattern-hint - tb.rulenode.hostname + rule-node-config.hostname - {{ 'tb.rulenode.hostname-required' | translate }} + {{ 'rule-node-config.hostname-required' | translate }} - tb.rulenode.device-id + rule-node-config.device-id - {{ 'tb.rulenode.device-id-required' | translate }} + {{ 'rule-node-config.device-id-required' | translate }} - tb.rulenode.credentials + rule-node-config.credentials {{ azureIotHubCredentialsTypeTranslationsMap.get(azureIotHubConfigForm.get('credentials.type').value) | translate }}
    - tb.rulenode.credentials-type + rule-node-config.credentials-type {{ azureIotHubCredentialsTypeTranslationsMap.get(credentialsType) | translate }} - {{ 'tb.rulenode.credentials-type-required' | translate }} + {{ 'rule-node-config.credentials-type-required' | translate }}
    @@ -63,20 +63,20 @@ - tb.rulenode.sas-key + rule-node-config.sas-key - {{ 'tb.rulenode.sas-key-required' | translate }} + {{ 'rule-node-config.sas-key-required' | translate }} + label="{{'rule-node-config.azure-ca-cert' | translate}}" + noFileText="rule-node-config.no-file" + dropLabel="{{'rule-node-config.drop-file' | translate}}"> @@ -84,9 +84,9 @@ inputId="caCertSelect" [existingFileName]="azureIotHubConfigForm.get('credentials.caCertFileName').value" (fileNameChanged)="azureIotHubConfigForm.get('credentials.caCertFileName').setValue($event)" - label="{{'tb.rulenode.azure-ca-cert' | translate}}" - noFileText="tb.rulenode.no-file" - dropLabel="{{'tb.rulenode.drop-file' | translate}}"> + label="{{'rule-node-config.azure-ca-cert' | translate}}" + noFileText="rule-node-config.no-file" + dropLabel="{{'rule-node-config.drop-file' | translate}}"> + label="{{'rule-node-config.cert' | translate}}" + noFileText="rule-node-config.no-file" + dropLabel="{{'rule-node-config.drop-file' | translate}}"> + label="{{'rule-node-config.private-key' | translate}}" + noFileText="rule-node-config.no-file" + dropLabel="{{'rule-node-config.drop-file' | translate}}"> - tb.rulenode.private-key-password + rule-node-config.private-key-password diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/kafka-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/kafka-config.component.html index 6bb1438a00..f23bbd353c 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/external/kafka-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/kafka-config.component.html @@ -17,56 +17,56 @@ -->
    - tb.rulenode.topic-pattern + rule-node-config.topic-pattern - {{ 'tb.rulenode.topic-pattern-required' | translate }} + {{ 'rule-node-config.topic-pattern-required' | translate }} - tb.rulenode.general-pattern-hint + rule-node-config.general-pattern-hint - tb.rulenode.key-pattern + rule-node-config.key-pattern - tb.rulenode.general-pattern-hint + rule-node-config.general-pattern-hint -
    tb.rulenode.key-pattern-hint
    +
    rule-node-config.key-pattern-hint
    - tb.rulenode.bootstrap-servers + rule-node-config.bootstrap-servers - {{ 'tb.rulenode.bootstrap-servers-required' | translate }} + {{ 'rule-node-config.bootstrap-servers-required' | translate }} - tb.rulenode.retries + rule-node-config.retries - {{ 'tb.rulenode.min-retries-message' | translate }} + {{ 'rule-node-config.min-retries-message' | translate }} - tb.rulenode.batch-size-bytes + rule-node-config.batch-size-bytes - {{ 'tb.rulenode.min-batch-size-bytes-message' | translate }} + {{ 'rule-node-config.min-batch-size-bytes-message' | translate }} - tb.rulenode.linger-ms + rule-node-config.linger-ms - {{ 'tb.rulenode.min-linger-ms-message' | translate }} + {{ 'rule-node-config.min-linger-ms-message' | translate }} - tb.rulenode.buffer-memory-bytes + rule-node-config.buffer-memory-bytes - {{ 'tb.rulenode.min-buffer-memory-bytes-message' | translate }} + {{ 'rule-node-config.min-buffer-memory-bytes-message' | translate }} - tb.rulenode.acks + rule-node-config.acks {{ ackValue }} @@ -74,34 +74,34 @@ - tb.rulenode.key-serializer + rule-node-config.key-serializer - {{ 'tb.rulenode.key-serializer-required' | translate }} + {{ 'rule-node-config.key-serializer-required' | translate }} - tb.rulenode.value-serializer + rule-node-config.value-serializer - {{ 'tb.rulenode.value-serializer-required' | translate }} + {{ 'rule-node-config.value-serializer-required' | translate }} - + + keyText="rule-node-config.key" + keyRequiredText="rule-node-config.key-required" + valText="rule-node-config.value" + valRequiredText="rule-node-config.value-required"> - {{ 'tb.rulenode.add-metadata-key-values-as-kafka-headers' | translate }} + {{ 'rule-node-config.add-metadata-key-values-as-kafka-headers' | translate }} -
    tb.rulenode.add-metadata-key-values-as-kafka-headers-hint
    +
    rule-node-config.add-metadata-key-values-as-kafka-headers-hint
    - tb.rulenode.charset-encoding + rule-node-config.charset-encoding {{ ToByteStandartCharsetTypeTranslationMap.get(charset) | translate }} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/lambda-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/lambda-config.component.html index a6907082fc..2ea8fa3957 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/external/lambda-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/lambda-config.component.html @@ -18,23 +18,23 @@
    -
    tb.rulenode.function-configuration
    +
    rule-node-config.function-configuration
    -
    - {{'tb.rulenode.function-name' | translate}} + {{'rule-node-config.function-name' | translate}} - {{'tb.rulenode.function-name-required' | translate}} + {{'rule-node-config.function-name-required' | translate}} - {{'tb.rulenode.qualifier' | translate}} + {{'rule-node-config.qualifier' | translate}} - tb.rulenode.qualifier-hint + rule-node-config.qualifier-hint
    @@ -42,28 +42,28 @@
    - tb.rulenode.aws-credentials + rule-node-config.aws-credentials
    - tb.rulenode.aws-access-key-id + rule-node-config.aws-access-key-id - {{ 'tb.rulenode.aws-access-key-id-required' | translate }} + {{ 'rule-node-config.aws-access-key-id-required' | translate }} - tb.rulenode.aws-secret-access-key + rule-node-config.aws-secret-access-key - {{ 'tb.rulenode.aws-secret-access-key-required' | translate }} + {{ 'rule-node-config.aws-secret-access-key-required' | translate }} - tb.rulenode.aws-region + rule-node-config.aws-region - {{ 'tb.rulenode.aws-region-required' | translate }} + {{ 'rule-node-config.aws-region-required' | translate }}
    @@ -72,41 +72,41 @@
    - tb.rulenode.advanced-settings + rule-node-config.advanced-settings
    - tb.rulenode.connection-timeout + rule-node-config.connection-timeout - {{ 'tb.rulenode.connection-timeout-required' | translate }} + {{ 'rule-node-config.connection-timeout-required' | translate }} - {{ 'tb.rulenode.connection-timeout-min' | translate }} + {{ 'rule-node-config.connection-timeout-min' | translate }} help + matTooltip="{{ 'rule-node-config.connection-timeout-hint' | translate }}">help - tb.rulenode.request-timeout + rule-node-config.request-timeout - {{ 'tb.rulenode.request-timeout-required' | translate }} + {{ 'rule-node-config.request-timeout-required' | translate }} - {{ 'tb.rulenode.request-timeout-min' | translate }} + {{ 'rule-node-config.request-timeout-min' | translate }} help + matTooltip="{{ 'rule-node-config.request-timeout-hint' | translate }}">help
    -
    - {{ 'tb.rulenode.tell-failure-aws-lambda' | translate }} + {{ 'rule-node-config.tell-failure-aws-lambda' | translate }}
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/mqtt-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/mqtt-config.component.html index 989a8829b7..a57da12587 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/external/mqtt-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/mqtt-config.component.html @@ -17,69 +17,69 @@ -->
    - tb.rulenode.topic-pattern + rule-node-config.topic-pattern - {{ 'tb.rulenode.topic-pattern-required' | translate }} + {{ 'rule-node-config.topic-pattern-required' | translate }} - tb.rulenode.general-pattern-hint + rule-node-config.general-pattern-hint
    - tb.rulenode.host + rule-node-config.host - {{ 'tb.rulenode.host-required' | translate }} + {{ 'rule-node-config.host-required' | translate }} - tb.rulenode.port + rule-node-config.port - {{ 'tb.rulenode.port-required' | translate }} + {{ 'rule-node-config.port-required' | translate }} - {{ 'tb.rulenode.port-range' | translate }} + {{ 'rule-node-config.port-range' | translate }} - {{ 'tb.rulenode.port-range' | translate }} + {{ 'rule-node-config.port-range' | translate }} - tb.rulenode.connect-timeout + rule-node-config.connect-timeout - {{ 'tb.rulenode.connect-timeout-required' | translate }} + {{ 'rule-node-config.connect-timeout-required' | translate }} - {{ 'tb.rulenode.connect-timeout-range' | translate }} + {{ 'rule-node-config.connect-timeout-range' | translate }} - {{ 'tb.rulenode.connect-timeout-range' | translate }} + {{ 'rule-node-config.connect-timeout-range' | translate }}
    - tb.rulenode.client-id + rule-node-config.client-id - {{'tb.rulenode.client-id-hint' | translate}} + {{'rule-node-config.client-id-hint' | translate}} - {{ 'tb.rulenode.append-client-id-suffix' | translate }} + {{ 'rule-node-config.append-client-id-suffix' | translate }} -
    {{ "tb.rulenode.client-id-suffix-hint" | translate }}
    +
    {{ "rule-node-config.client-id-suffix-hint" | translate }}
    - {{ 'tb.rulenode.parse-to-plain-text' | translate }} + {{ 'rule-node-config.parse-to-plain-text' | translate }} -
    {{ "tb.rulenode.parse-to-plain-text-hint" | translate }}
    +
    {{ "rule-node-config.parse-to-plain-text-hint" | translate }}
    - {{ 'tb.rulenode.clean-session' | translate }} + {{ 'rule-node-config.clean-session' | translate }} - {{ "tb.rulenode.retained-message" | translate }} + {{ "rule-node-config.retained-message" | translate }} - {{ 'tb.rulenode.enable-ssl' | translate }} + {{ 'rule-node-config.enable-ssl' | translate }}
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/pubsub-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/pubsub-config.component.html index eb5c700400..b3d303765a 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/external/pubsub-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/pubsub-config.component.html @@ -17,17 +17,17 @@ -->
    - tb.rulenode.gcp-project-id + rule-node-config.gcp-project-id - {{ 'tb.rulenode.gcp-project-id-required' | translate }} + {{ 'rule-node-config.gcp-project-id-required' | translate }} - tb.rulenode.pubsub-topic-name + rule-node-config.pubsub-topic-name - {{ 'tb.rulenode.pubsub-topic-name-required' | translate }} + {{ 'rule-node-config.pubsub-topic-name-required' | translate }} + label="{{'rule-node-config.gcp-service-account-key' | translate}}" + noFileText="rule-node-config.no-file" + dropLabel="{{'rule-node-config.drop-file' | translate}}"> - -
    + +
    + keyText="rule-node-config.name" + keyRequiredText="rule-node-config.name-required" + valText="rule-node-config.value" + valRequiredText="rule-node-config.value-required">
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/rabbit-mq-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/rabbit-mq-config.component.html index 0ef6b40877..c04b0be902 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/external/rabbit-mq-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/rabbit-mq-config.component.html @@ -17,15 +17,15 @@ -->
    - tb.rulenode.exchange-name-pattern + rule-node-config.exchange-name-pattern - tb.rulenode.routing-key-pattern + rule-node-config.routing-key-pattern - tb.rulenode.message-properties + rule-node-config.message-properties {{ property }} @@ -34,63 +34,63 @@
    - tb.rulenode.host + rule-node-config.host - {{ 'tb.rulenode.host-required' | translate }} + {{ 'rule-node-config.host-required' | translate }} - tb.rulenode.port + rule-node-config.port - {{ 'tb.rulenode.port-required' | translate }} + {{ 'rule-node-config.port-required' | translate }} - {{ 'tb.rulenode.port-range' | translate }} + {{ 'rule-node-config.port-range' | translate }} - {{ 'tb.rulenode.port-range' | translate }} + {{ 'rule-node-config.port-range' | translate }}
    - tb.rulenode.virtual-host + rule-node-config.virtual-host - tb.rulenode.username + rule-node-config.username - tb.rulenode.password + rule-node-config.password - {{ 'tb.rulenode.automatic-recovery' | translate }} + {{ 'rule-node-config.automatic-recovery' | translate }} - tb.rulenode.connection-timeout-ms + rule-node-config.connection-timeout-ms - {{ 'tb.rulenode.min-connection-timeout-ms-message' | translate }} + {{ 'rule-node-config.min-connection-timeout-ms-message' | translate }} - tb.rulenode.handshake-timeout-ms + rule-node-config.handshake-timeout-ms - {{ 'tb.rulenode.min-handshake-timeout-ms-message' | translate }} + {{ 'rule-node-config.min-handshake-timeout-ms-message' | translate }} - + + keyText="rule-node-config.key" + keyRequiredText="rule-node-config.key-required" + valText="rule-node-config.value" + valRequiredText="rule-node-config.value-required">
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/rest-api-call-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/rest-api-call-config.component.html index 5dfca5144a..0bae38471f 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/external/rest-api-call-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/rest-api-call-config.component.html @@ -17,15 +17,15 @@ -->
    - tb.rulenode.endpoint-url-pattern + rule-node-config.endpoint-url-pattern - {{ 'tb.rulenode.endpoint-url-pattern-required' | translate }} + {{ 'rule-node-config.endpoint-url-pattern-required' | translate }} - tb.rulenode.general-pattern-hint + rule-node-config.general-pattern-hint - tb.rulenode.request-method + rule-node-config.request-method {{ requestType }} @@ -33,26 +33,26 @@ - {{ 'tb.rulenode.enable-proxy' | translate }} + {{ 'rule-node-config.enable-proxy' | translate }} - {{ 'tb.rulenode.use-simple-client-http-factory' | translate }} + {{ 'rule-node-config.use-simple-client-http-factory' | translate }} - {{ 'tb.rulenode.parse-to-plain-text' | translate }} + {{ 'rule-node-config.parse-to-plain-text' | translate }} -
    tb.rulenode.parse-to-plain-text-hint
    +
    rule-node-config.parse-to-plain-text-hint
    - {{ 'tb.rulenode.ignore-request-body' | translate }} + {{ 'rule-node-config.ignore-request-body' | translate }}
    - {{ 'tb.rulenode.use-system-proxy-properties' | translate }} + {{ 'rule-node-config.use-system-proxy-properties' | translate }}
    - tb.rulenode.proxy-scheme + rule-node-config.proxy-scheme {{ proxyScheme }} @@ -60,71 +60,71 @@ - tb.rulenode.proxy-host + rule-node-config.proxy-host - {{ 'tb.rulenode.proxy-host-required' | translate }} + {{ 'rule-node-config.proxy-host-required' | translate }} - tb.rulenode.proxy-port + rule-node-config.proxy-port - {{ 'tb.rulenode.proxy-port-required' | translate }} + {{ 'rule-node-config.proxy-port-required' | translate }} - {{ 'tb.rulenode.proxy-port-range' | translate }} + {{ 'rule-node-config.proxy-port-range' | translate }}
    - tb.rulenode.proxy-user + rule-node-config.proxy-user - tb.rulenode.proxy-password + rule-node-config.proxy-password
    - tb.rulenode.read-timeout + rule-node-config.read-timeout - tb.rulenode.read-timeout-hint + rule-node-config.read-timeout-hint - {{ 'tb.rulenode.int-range' | translate }} + {{ 'rule-node-config.int-range' | translate }} - tb.rulenode.max-parallel-requests-count + rule-node-config.max-parallel-requests-count - tb.rulenode.max-parallel-requests-count-hint + rule-node-config.max-parallel-requests-count-hint - {{ 'tb.rulenode.int-range' | translate }} + {{ 'rule-node-config.int-range' | translate }} - tb.rulenode.max-response-size + rule-node-config.max-response-size - tb.rulenode.max-response-size-hint + rule-node-config.max-response-size-hint - {{ 'tb.rulenode.memory-buffer-size-range' | translate: { max: MemoryBufferSizeInKbLimit } }} + {{ 'rule-node-config.memory-buffer-size-range' | translate: { max: MemoryBufferSizeInKbLimit } }} - -
    + +
    + keyText="rule-node-config.header" + keyRequiredText="rule-node-config.header-required" + valText="rule-node-config.value" + valRequiredText="rule-node-config.value-required">
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/send-email-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/send-email-config.component.html index cbc4ec7a4a..f24c20038b 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/external/send-email-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/send-email-config.component.html @@ -17,11 +17,11 @@ -->
    - {{ 'tb.rulenode.use-system-smtp-settings' | translate }} + {{ 'rule-node-config.use-system-smtp-settings' | translate }}
    - tb.rulenode.smtp-protocol + rule-node-config.smtp-protocol {{ smtpProtocol.toUpperCase() }} @@ -30,41 +30,41 @@
    - tb.rulenode.smtp-host + rule-node-config.smtp-host - {{ 'tb.rulenode.smtp-host-required' | translate }} + {{ 'rule-node-config.smtp-host-required' | translate }} - tb.rulenode.smtp-port + rule-node-config.smtp-port - {{ 'tb.rulenode.smtp-port-required' | translate }} + {{ 'rule-node-config.smtp-port-required' | translate }} - {{ 'tb.rulenode.smtp-port-range' | translate }} + {{ 'rule-node-config.smtp-port-range' | translate }} - {{ 'tb.rulenode.smtp-port-range' | translate }} + {{ 'rule-node-config.smtp-port-range' | translate }}
    - tb.rulenode.timeout-msec + rule-node-config.timeout-msec - {{ 'tb.rulenode.timeout-required' | translate }} + {{ 'rule-node-config.timeout-required' | translate }} - {{ 'tb.rulenode.min-timeout-msec-message' | translate }} + {{ 'rule-node-config.min-timeout-msec-message' | translate }} - {{ 'tb.rulenode.enable-tls' | translate }} + {{ 'rule-node-config.enable-tls' | translate }} - tb.rulenode.tls-version + rule-node-config.tls-version {{ tlsVersion }} @@ -72,44 +72,44 @@ - {{ 'tb.rulenode.enable-proxy' | translate }} + {{ 'rule-node-config.enable-proxy' | translate }}
    - tb.rulenode.proxy-host + rule-node-config.proxy-host - {{ 'tb.rulenode.proxy-host-required' | translate }} + {{ 'rule-node-config.proxy-host-required' | translate }} - tb.rulenode.proxy-port + rule-node-config.proxy-port - {{ 'tb.rulenode.proxy-port-required' | translate }} + {{ 'rule-node-config.proxy-port-required' | translate }} - {{ 'tb.rulenode.proxy-port-range' | translate }} + {{ 'rule-node-config.proxy-port-range' | translate }}
    - tb.rulenode.proxy-user + rule-node-config.proxy-user - tb.rulenode.proxy-password + rule-node-config.proxy-password
    - tb.rulenode.username - + rule-node-config.username + - tb.rulenode.password - + rule-node-config.password +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/send-sms-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/send-sms-config.component.html index 12f9208c5d..3b4e63308a 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/external/send-sms-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/send-sms-config.component.html @@ -17,23 +17,23 @@ -->
    - tb.rulenode.numbers-to-template + rule-node-config.numbers-to-template - {{ 'tb.rulenode.numbers-to-template-required' | translate }} + {{ 'rule-node-config.numbers-to-template-required' | translate }} - + - tb.rulenode.sms-message-template + rule-node-config.sms-message-template - {{ 'tb.rulenode.sms-message-template-required' | translate }} + {{ 'rule-node-config.sms-message-template-required' | translate }} - tb.rulenode.general-pattern-hint + rule-node-config.general-pattern-hint - {{ 'tb.rulenode.use-system-sms-settings' | translate }} + {{ 'rule-node-config.use-system-sms-settings' | translate }}
    - tb.rulenode.message-template + rule-node-config.message-template - {{ 'tb.rulenode.message-template-required' | translate }} + {{ 'rule-node-config.message-template-required' | translate }} - tb.rulenode.general-pattern-hint + rule-node-config.general-pattern-hint - {{ 'tb.rulenode.use-system-slack-settings' | translate }} + {{ 'rule-node-config.use-system-slack-settings' | translate }} - tb.rulenode.slack-api-token + rule-node-config.slack-api-token - {{ 'tb.rulenode.slack-api-token-required' | translate }} + {{ 'rule-node-config.slack-api-token-required' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/sns-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/sns-config.component.html index f4e4aa738d..a5be9017a3 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/external/sns-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/sns-config.component.html @@ -17,32 +17,32 @@ -->
    - tb.rulenode.topic-arn-pattern + rule-node-config.topic-arn-pattern - {{ 'tb.rulenode.topic-arn-pattern-required' | translate }} + {{ 'rule-node-config.topic-arn-pattern-required' | translate }} - tb.rulenode.general-pattern-hint + rule-node-config.general-pattern-hint - tb.rulenode.aws-access-key-id + rule-node-config.aws-access-key-id - {{ 'tb.rulenode.aws-access-key-id-required' | translate }} + {{ 'rule-node-config.aws-access-key-id-required' | translate }} - tb.rulenode.aws-secret-access-key + rule-node-config.aws-secret-access-key - {{ 'tb.rulenode.aws-secret-access-key-required' | translate }} + {{ 'rule-node-config.aws-secret-access-key-required' | translate }} - tb.rulenode.aws-region + rule-node-config.aws-region - {{ 'tb.rulenode.aws-region-required' | translate }} + {{ 'rule-node-config.aws-region-required' | translate }}
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/sqs-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/sqs-config.component.html index a7272e6d66..2956cc4ab0 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/external/sqs-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/sqs-config.component.html @@ -17,7 +17,7 @@ -->
    - tb.rulenode.queue-type + rule-node-config.queue-type {{ sqsQueueTypeTranslationsMap.get(type) | translate }} @@ -25,52 +25,52 @@ - tb.rulenode.queue-url-pattern + rule-node-config.queue-url-pattern - {{ 'tb.rulenode.queue-url-pattern-required' | translate }} + {{ 'rule-node-config.queue-url-pattern-required' | translate }} - tb.rulenode.general-pattern-hint + rule-node-config.general-pattern-hint - tb.rulenode.delay-seconds + rule-node-config.delay-seconds - {{ 'tb.rulenode.min-delay-seconds-message' | translate }} + {{ 'rule-node-config.min-delay-seconds-message' | translate }} - {{ 'tb.rulenode.max-delay-seconds-message' | translate }} + {{ 'rule-node-config.max-delay-seconds-message' | translate }} - -
    + +
    + keyText="rule-node-config.name" + keyRequiredText="rule-node-config.name-required" + valText="rule-node-config.value" + valRequiredText="rule-node-config.value-required"> - tb.rulenode.aws-access-key-id + rule-node-config.aws-access-key-id - {{ 'tb.rulenode.aws-access-key-id-required' | translate }} + {{ 'rule-node-config.aws-access-key-id-required' | translate }} - tb.rulenode.aws-secret-access-key + rule-node-config.aws-secret-access-key - {{ 'tb.rulenode.aws-secret-access-key-required' | translate }} + {{ 'rule-node-config.aws-secret-access-key-required' | translate }} - tb.rulenode.aws-region + rule-node-config.aws-region - {{ 'tb.rulenode.aws-region-required' | translate }} + {{ 'rule-node-config.aws-region-required' | translate }}
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/check-alarm-status.component.html b/ui-ngx/src/app/modules/home/components/rule-node/filter/check-alarm-status.component.html index 329b876e1e..b8f0faf15f 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/filter/check-alarm-status.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/check-alarm-status.component.html @@ -17,9 +17,9 @@ -->
    -
    tb.rulenode.alarm-status
    +
    rule-node-config.alarm-status
    - tb.rulenode.alarm-required + rule-node-config.alarm-required
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/check-message-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/filter/check-message-config.component.html index 7ded71d798..6566912570 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/filter/check-message-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/check-message-config.component.html @@ -17,29 +17,29 @@ -->
    -
    tb.rulenode.fields-to-check
    +
    rule-node-config.fields-to-check
    - tb.rulenode.at-least-one-field-required + rule-node-config.at-least-one-field-required
    help + matTooltip="{{ 'rule-node-config.chip-help' | translate: { inputName: 'rule-node-config.field-name' | translate } }}">help help + matTooltip="{{ 'rule-node-config.chip-help' | translate: { inputName: 'rule-node-config.field-name' | translate } }}">help -
    - {{ 'tb.rulenode.check-all-keys' | translate }} + {{ 'rule-node-config.check-all-keys' | translate }}
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/check-relation-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/filter/check-relation-config.component.html index 2a4b61792e..b9763b280f 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/filter/check-relation-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/check-relation-config.component.html @@ -16,13 +16,13 @@ -->
    -
    tb.rulenode.relation-search-parameters
    +
    rule-node-config.relation-search-parameters
    {{ 'relation.direction' | translate }} - {{ entitySearchDirectionTranslationsMap.get(direction) | translate }} tb.rulenode.relations-query-config-direction-suffix + {{ entitySearchDirectionTranslationsMap.get(direction) | translate }} rule-node-config.relations-query-config-direction-suffix @@ -30,10 +30,10 @@ required formControlName="relationType"> -
    - {{ 'tb.rulenode.check-relation-to-specific-entity' | translate }} + {{ 'rule-node-config.check-relation-to-specific-entity' | translate }}
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/gps-geo-filter-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/filter/gps-geo-filter-config.component.html index 738e834450..ac09e2156a 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/filter/gps-geo-filter-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/gps-geo-filter-config.component.html @@ -17,32 +17,32 @@ -->
    -
    tb.rulenode.coordinate-field-names
    +
    rule-node-config.coordinate-field-names
    - {{ 'tb.rulenode.latitude-field-name' | translate }} + {{ 'rule-node-config.latitude-field-name' | translate }} - {{ 'tb.rulenode.latitude-field-name-required' | translate }} + {{ 'rule-node-config.latitude-field-name-required' | translate }} - {{ 'tb.rulenode.longitude-field-name' | translate }} + {{ 'rule-node-config.longitude-field-name' | translate }} - {{ 'tb.rulenode.longitude-field-name-required' | translate }} + {{ 'rule-node-config.longitude-field-name-required' | translate }}
    -
    tb.rulenode.coordinate-field-hint
    +
    rule-node-config.coordinate-field-hint
    -
    tb.rulenode.geofence-configuration
    +
    rule-node-config.geofence-configuration
    - {{ 'tb.rulenode.perimeter-type' | translate }} + {{ 'rule-node-config.perimeter-type' | translate }} {{ perimeterTypeTranslationMap.get(type) | translate }} @@ -50,63 +50,63 @@
    - {{ 'tb.rulenode.fetch-perimeter-info-from-metadata' | translate }} + {{ 'rule-node-config.fetch-perimeter-info-from-metadata' | translate }}
    - {{ 'tb.rulenode.perimeter-key-name' | translate }} + {{ 'rule-node-config.perimeter-key-name' | translate }} - {{ 'tb.rulenode.perimeter-key-name-required' | translate }} + {{ 'rule-node-config.perimeter-key-name-required' | translate }} - {{ 'tb.rulenode.perimeter-key-name-hint' | translate }} + {{ 'rule-node-config.perimeter-key-name-hint' | translate }}
    - {{ 'tb.rulenode.circle-center-latitude' | translate }} + {{ 'rule-node-config.circle-center-latitude' | translate }} - {{ 'tb.rulenode.circle-center-latitude-required' | translate }} + {{ 'rule-node-config.circle-center-latitude-required' | translate }} - {{ 'tb.rulenode.circle-center-longitude' | translate }} + {{ 'rule-node-config.circle-center-longitude' | translate }} - {{ 'tb.rulenode.circle-center-longitude-required' | translate }} + {{ 'rule-node-config.circle-center-longitude-required' | translate }}
    - {{ 'tb.rulenode.range' | translate }} + {{ 'rule-node-config.range' | translate }} - {{ 'tb.rulenode.range-required' | translate }} + {{ 'rule-node-config.range-required' | translate }} - {{ 'tb.rulenode.range-units' | translate }} + {{ 'rule-node-config.range-units' | translate }} {{ rangeUnitTranslationMap.get(type) | translate }} - {{ 'tb.rulenode.range-units-required' | translate }} + {{ 'rule-node-config.range-units-required' | translate }}
    @@ -114,11 +114,11 @@ - {{ 'tb.rulenode.polygon-definition' | translate }} + {{ 'rule-node-config.polygon-definition' | translate }} - {{ 'tb.rulenode.polygon-definition-hint' | translate }} + {{ 'rule-node-config.polygon-definition-hint' | translate }} - {{ 'tb.rulenode.polygon-definition-required' | translate }} + {{ 'rule-node-config.polygon-definition-required' | translate }}
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/message-type-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/filter/message-type-config.component.html index 1b347ca1d1..847ab255f5 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/filter/message-type-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/message-type-config.component.html @@ -18,7 +18,7 @@
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/originator-type-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/filter/originator-type-config.component.html index 233106bd8e..1dc3cf34a2 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/filter/originator-type-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/originator-type-config.component.html @@ -20,12 +20,12 @@ formControlName="originatorTypes" [allowedEntityTypes]="allowedEntityTypes" [ignoreAuthorityFilter]="true" - [emptyInputPlaceholder]="'tb.rulenode.add-entity-type' | translate" - [filledInputPlaceholder]="'tb.rulenode.add-entity-type' | translate" - [label]="'tb.rulenode.select-entity-types' | translate" + [emptyInputPlaceholder]="'rule-node-config.add-entity-type' | translate" + [filledInputPlaceholder]="'rule-node-config.add-entity-type' | translate" + [label]="'rule-node-config.select-entity-types' | translate" required> help + matTooltip="{{ 'rule-node-config.chip-help' | translate: { inputName: 'rule-node-config.entity-type' | translate } }}">help
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/script-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/filter/script-config.component.ts index 643f66448f..a5e4ffdea6 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/filter/script-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/script-config.component.ts @@ -43,7 +43,7 @@ export class ScriptConfigComponent extends RuleNodeConfigurationComponent { readonly hasScript = true; - readonly testScriptLabel = 'tb.rulenode.test-filter-function'; + readonly testScriptLabel = 'rule-node-config.test-filter-function'; constructor(protected store: Store, private fb: UntypedFormBuilder, @@ -104,7 +104,7 @@ export class ScriptConfigComponent extends RuleNodeConfigurationComponent { this.nodeScriptTestService.testNodeScript( script, 'filter', - this.translate.instant('tb.rulenode.filter'), + this.translate.instant('rule-node-config.filter'), 'Filter', ['msg', 'metadata', 'msgType'], this.ruleNodeId, diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/switch-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/filter/switch-config.component.ts index a77e5d3e29..c444effd08 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/filter/switch-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/switch-config.component.ts @@ -46,7 +46,7 @@ export class SwitchConfigComponent extends RuleNodeConfigurationComponent { readonly hasScript = true; - readonly testScriptLabel = 'tb.rulenode.test-switch-function'; + readonly testScriptLabel = 'rule-node-config.test-switch-function'; constructor(private fb: UntypedFormBuilder, private nodeScriptTestService: NodeScriptTestService, @@ -106,7 +106,7 @@ export class SwitchConfigComponent extends RuleNodeConfigurationComponent { this.nodeScriptTestService.testNodeScript( script, 'switch', - this.translate.instant('tb.rulenode.switch'), + this.translate.instant('rule-node-config.switch'), 'Switch', ['msg', 'metadata', 'msgType'], this.ruleNodeId, diff --git a/ui-ngx/src/app/modules/home/components/rule-node/flow/rule-chain-input.component.html b/ui-ngx/src/app/modules/home/components/rule-node/flow/rule-chain-input.component.html index a91b9980d4..6bee899138 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/flow/rule-chain-input.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/flow/rule-chain-input.component.html @@ -17,10 +17,10 @@ -->
    -
    - {{ 'tb.rulenode.forward-msg-default-rule-chain' | translate }} + {{ 'rule-node-config.forward-msg-default-rule-chain' | translate }}
    -
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.models.ts b/ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.models.ts index 6562a732aa..a358bbaf12 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.models.ts @@ -34,27 +34,27 @@ export interface OriginatorValuesDescriptions { export const originatorSourceTranslations = new Map( [ - [OriginatorSource.CUSTOMER, 'tb.rulenode.originator-customer'], - [OriginatorSource.TENANT, 'tb.rulenode.originator-tenant'], - [OriginatorSource.RELATED, 'tb.rulenode.originator-related'], - [OriginatorSource.ALARM_ORIGINATOR, 'tb.rulenode.originator-alarm-originator'], - [OriginatorSource.ENTITY, 'tb.rulenode.originator-entity'], + [OriginatorSource.CUSTOMER, 'rule-node-config.originator-customer'], + [OriginatorSource.TENANT, 'rule-node-config.originator-tenant'], + [OriginatorSource.RELATED, 'rule-node-config.originator-related'], + [OriginatorSource.ALARM_ORIGINATOR, 'rule-node-config.originator-alarm-originator'], + [OriginatorSource.ENTITY, 'rule-node-config.originator-entity'], ] ); export const originatorSourceDescTranslations = new Map( [ - [OriginatorSource.CUSTOMER, 'tb.rulenode.originator-customer-desc'], - [OriginatorSource.TENANT, 'tb.rulenode.originator-tenant-desc'], - [OriginatorSource.RELATED, 'tb.rulenode.originator-related-entity-desc'], - [OriginatorSource.ALARM_ORIGINATOR, 'tb.rulenode.originator-alarm-originator-desc'], - [OriginatorSource.ENTITY, 'tb.rulenode.originator-entity-by-name-pattern-desc'], + [OriginatorSource.CUSTOMER, 'rule-node-config.originator-customer-desc'], + [OriginatorSource.TENANT, 'rule-node-config.originator-tenant-desc'], + [OriginatorSource.RELATED, 'rule-node-config.originator-related-entity-desc'], + [OriginatorSource.ALARM_ORIGINATOR, 'rule-node-config.originator-alarm-originator-desc'], + [OriginatorSource.ENTITY, 'rule-node-config.originator-entity-by-name-pattern-desc'], ] ); export const allowedOriginatorFields: EntityField[] = [ entityFields.createdTime, entityFields.name, - {value: 'type', name: 'tb.rulenode.profile-name', keyName: 'originatorProfileName'}, + {value: 'type', name: 'rule-node-config.profile-name', keyName: 'originatorProfileName'}, entityFields.firstName, entityFields.lastName, entityFields.email, @@ -67,8 +67,8 @@ export const allowedOriginatorFields: EntityField[] = [ entityFields.zip, entityFields.phone, entityFields.label, - {value: 'id', name: 'tb.rulenode.id', keyName: 'id'}, - {value: 'additionalInfo', name: 'tb.rulenode.additional-info', keyName: 'additionalInfo'} + {value: 'id', name: 'rule-node-config.id', keyName: 'id'}, + {value: 'additionalInfo', name: 'rule-node-config.additional-info', keyName: 'additionalInfo'} ]; export const OriginatorFieldsMappingValues = new Map( @@ -100,8 +100,8 @@ export enum PerimeterType { export const perimeterTypeTranslations = new Map( [ - [PerimeterType.CIRCLE, 'tb.rulenode.perimeter-circle'], - [PerimeterType.POLYGON, 'tb.rulenode.perimeter-polygon'], + [PerimeterType.CIRCLE, 'rule-node-config.perimeter-circle'], + [PerimeterType.POLYGON, 'rule-node-config.perimeter-polygon'], ] ); @@ -115,11 +115,11 @@ export enum TimeUnit { export const timeUnitTranslations = new Map( [ - [TimeUnit.MILLISECONDS, 'tb.rulenode.time-unit-milliseconds'], - [TimeUnit.SECONDS, 'tb.rulenode.time-unit-seconds'], - [TimeUnit.MINUTES, 'tb.rulenode.time-unit-minutes'], - [TimeUnit.HOURS, 'tb.rulenode.time-unit-hours'], - [TimeUnit.DAYS, 'tb.rulenode.time-unit-days'] + [TimeUnit.MILLISECONDS, 'rule-node-config.time-unit-milliseconds'], + [TimeUnit.SECONDS, 'rule-node-config.time-unit-seconds'], + [TimeUnit.MINUTES, 'rule-node-config.time-unit-minutes'], + [TimeUnit.HOURS, 'rule-node-config.time-unit-hours'], + [TimeUnit.DAYS, 'rule-node-config.time-unit-days'] ] ); @@ -133,11 +133,11 @@ export enum RangeUnit { export const rangeUnitTranslations = new Map( [ - [RangeUnit.METER, 'tb.rulenode.range-unit-meter'], - [RangeUnit.KILOMETER, 'tb.rulenode.range-unit-kilometer'], - [RangeUnit.FOOT, 'tb.rulenode.range-unit-foot'], - [RangeUnit.MILE, 'tb.rulenode.range-unit-mile'], - [RangeUnit.NAUTICAL_MILE, 'tb.rulenode.range-unit-nautical-mile'] + [RangeUnit.METER, 'rule-node-config.range-unit-meter'], + [RangeUnit.KILOMETER, 'rule-node-config.range-unit-kilometer'], + [RangeUnit.FOOT, 'rule-node-config.range-unit-foot'], + [RangeUnit.MILE, 'rule-node-config.range-unit-mile'], + [RangeUnit.NAUTICAL_MILE, 'rule-node-config.range-unit-nautical-mile'] ] ); @@ -162,17 +162,17 @@ export interface SvMapOption { export const entityDetailsTranslations = new Map( [ - [EntityDetailsField.ID, 'tb.rulenode.entity-details-id'], - [EntityDetailsField.TITLE, 'tb.rulenode.entity-details-title'], - [EntityDetailsField.COUNTRY, 'tb.rulenode.entity-details-country'], - [EntityDetailsField.STATE, 'tb.rulenode.entity-details-state'], - [EntityDetailsField.CITY, 'tb.rulenode.entity-details-city'], - [EntityDetailsField.ZIP, 'tb.rulenode.entity-details-zip'], - [EntityDetailsField.ADDRESS, 'tb.rulenode.entity-details-address'], - [EntityDetailsField.ADDRESS2, 'tb.rulenode.entity-details-address2'], - [EntityDetailsField.PHONE, 'tb.rulenode.entity-details-phone'], - [EntityDetailsField.EMAIL, 'tb.rulenode.entity-details-email'], - [EntityDetailsField.ADDITIONAL_INFO, 'tb.rulenode.entity-details-additional_info'] + [EntityDetailsField.ID, 'rule-node-config.entity-details-id'], + [EntityDetailsField.TITLE, 'rule-node-config.entity-details-title'], + [EntityDetailsField.COUNTRY, 'rule-node-config.entity-details-country'], + [EntityDetailsField.STATE, 'rule-node-config.entity-details-state'], + [EntityDetailsField.CITY, 'rule-node-config.entity-details-city'], + [EntityDetailsField.ZIP, 'rule-node-config.entity-details-zip'], + [EntityDetailsField.ADDRESS, 'rule-node-config.entity-details-address'], + [EntityDetailsField.ADDRESS2, 'rule-node-config.entity-details-address2'], + [EntityDetailsField.PHONE, 'rule-node-config.entity-details-phone'], + [EntityDetailsField.EMAIL, 'rule-node-config.entity-details-email'], + [EntityDetailsField.ADDITIONAL_INFO, 'rule-node-config.entity-details-additional_info'] ] ); @@ -184,17 +184,17 @@ export enum FetchMode { export const deduplicationStrategiesTranslations = new Map( [ - [FetchMode.FIRST, 'tb.rulenode.first'], - [FetchMode.LAST, 'tb.rulenode.last'], - [FetchMode.ALL, 'tb.rulenode.all'] + [FetchMode.FIRST, 'rule-node-config.first'], + [FetchMode.LAST, 'rule-node-config.last'], + [FetchMode.ALL, 'rule-node-config.all'] ] ); export const deduplicationStrategiesHintTranslations = new Map( [ - [FetchMode.FIRST, 'tb.rulenode.first-mode-hint'], - [FetchMode.LAST, 'tb.rulenode.last-mode-hint'], - [FetchMode.ALL, 'tb.rulenode.all-mode-hint'] + [FetchMode.FIRST, 'rule-node-config.first-mode-hint'], + [FetchMode.LAST, 'rule-node-config.last-mode-hint'], + [FetchMode.ALL, 'rule-node-config.all-mode-hint'] ] ); @@ -211,24 +211,24 @@ export enum DataToFetch { export const dataToFetchTranslations = new Map( [ - [DataToFetch.ATTRIBUTES, 'tb.rulenode.attributes'], - [DataToFetch.LATEST_TELEMETRY, 'tb.rulenode.latest-telemetry'], - [DataToFetch.FIELDS, 'tb.rulenode.fields'] + [DataToFetch.ATTRIBUTES, 'rule-node-config.attributes'], + [DataToFetch.LATEST_TELEMETRY, 'rule-node-config.latest-telemetry'], + [DataToFetch.FIELDS, 'rule-node-config.fields'] ] ); export const msgMetadataLabelTranslations = new Map( [ - [DataToFetch.ATTRIBUTES, 'tb.rulenode.add-mapped-attribute-to'], - [DataToFetch.LATEST_TELEMETRY, 'tb.rulenode.add-mapped-latest-telemetry-to'], - [DataToFetch.FIELDS, 'tb.rulenode.add-mapped-fields-to'] + [DataToFetch.ATTRIBUTES, 'rule-node-config.add-mapped-attribute-to'], + [DataToFetch.LATEST_TELEMETRY, 'rule-node-config.add-mapped-latest-telemetry-to'], + [DataToFetch.FIELDS, 'rule-node-config.add-mapped-fields-to'] ] ); export const samplingOrderTranslations = new Map( [ - [SamplingOrder.ASC, 'tb.rulenode.ascending'], - [SamplingOrder.DESC, 'tb.rulenode.descending'] + [SamplingOrder.ASC, 'rule-node-config.ascending'], + [SamplingOrder.DESC, 'rule-node-config.descending'] ] ); @@ -239,8 +239,8 @@ export enum SqsQueueType { export const sqsQueueTypeTranslations = new Map( [ - [SqsQueueType.STANDARD, 'tb.rulenode.sqs-queue-standard'], - [SqsQueueType.FIFO, 'tb.rulenode.sqs-queue-fifo'], + [SqsQueueType.STANDARD, 'rule-node-config.sqs-queue-standard'], + [SqsQueueType.FIFO, 'rule-node-config.sqs-queue-fifo'], ] ); @@ -249,9 +249,9 @@ export const credentialsTypes: credentialsType[] = ['anonymous', 'basic', 'cert. export const credentialsTypeTranslations = new Map( [ - ['anonymous', 'tb.rulenode.credentials-anonymous'], - ['basic', 'tb.rulenode.credentials-basic'], - ['cert.PEM', 'tb.rulenode.credentials-pem'] + ['anonymous', 'rule-node-config.credentials-anonymous'], + ['basic', 'rule-node-config.credentials-basic'], + ['cert.PEM', 'rule-node-config.credentials-pem'] ] ); @@ -260,8 +260,8 @@ export const azureIotHubCredentialsTypes: AzureIotHubCredentialsType[] = ['sas', export const azureIotHubCredentialsTypeTranslations = new Map( [ - ['sas', 'tb.rulenode.credentials-sas'], - ['cert.PEM', 'tb.rulenode.credentials-pem'] + ['sas', 'rule-node-config.credentials-sas'], + ['cert.PEM', 'rule-node-config.credentials-pem'] ] ); @@ -283,12 +283,12 @@ export const ToByteStandartCharsetTypes = [ export const ToByteStandartCharsetTypeTranslations = new Map( [ - ['US-ASCII', 'tb.rulenode.charset-us-ascii'], - ['ISO-8859-1', 'tb.rulenode.charset-iso-8859-1'], - ['UTF-8', 'tb.rulenode.charset-utf-8'], - ['UTF-16BE', 'tb.rulenode.charset-utf-16be'], - ['UTF-16LE', 'tb.rulenode.charset-utf-16le'], - ['UTF-16', 'tb.rulenode.charset-utf-16'], + ['US-ASCII', 'rule-node-config.charset-us-ascii'], + ['ISO-8859-1', 'rule-node-config.charset-iso-8859-1'], + ['UTF-8', 'rule-node-config.charset-utf-8'], + ['UTF-16BE', 'rule-node-config.charset-utf-16be'], + ['UTF-16LE', 'rule-node-config.charset-utf-16le'], + ['UTF-16', 'rule-node-config.charset-utf-16'], ] ); @@ -720,23 +720,23 @@ export enum FetchTo { } export const FetchFromToTranslation = new Map([ - [FetchTo.DATA, 'tb.rulenode.message-to-metadata'], - [FetchTo.METADATA, 'tb.rulenode.metadata-to-message'], + [FetchTo.DATA, 'rule-node-config.message-to-metadata'], + [FetchTo.METADATA, 'rule-node-config.metadata-to-message'], ]); export const FetchFromTranslation = new Map([ - [FetchTo.DATA, 'tb.rulenode.from-message'], - [FetchTo.METADATA, 'tb.rulenode.from-metadata'], + [FetchTo.DATA, 'rule-node-config.from-message'], + [FetchTo.METADATA, 'rule-node-config.from-metadata'], ]); export const FetchToTranslation = new Map([ - [FetchTo.DATA, 'tb.rulenode.message'], - [FetchTo.METADATA, 'tb.rulenode.metadata'], + [FetchTo.DATA, 'rule-node-config.message'], + [FetchTo.METADATA, 'rule-node-config.metadata'], ]); export const FetchToRenameTranslation = new Map([ - [FetchTo.DATA, 'tb.rulenode.message'], - [FetchTo.METADATA, 'tb.rulenode.message-metadata'], + [FetchTo.DATA, 'rule-node-config.message'], + [FetchTo.METADATA, 'rule-node-config.message-metadata'], ]); export interface ArgumentTypeData { @@ -748,36 +748,36 @@ export const ArgumentTypeMap = new Map([ [ ArgumentType.MESSAGE_BODY, { - name: 'tb.rulenode.message-body-type', - description: 'Fetch argument value from incoming message' + name: 'rule-node-config.message-body-type', + description: 'rule-node-config.message-body-type-description' } ], [ ArgumentType.MESSAGE_METADATA, { - name: 'tb.rulenode.message-metadata-type', - description: 'Fetch argument value from incoming message metadata' + name: 'rule-node-config.message-metadata-type', + description: 'rule-node-config.message-metadata-type-description' } ], [ ArgumentType.ATTRIBUTE, { - name: 'tb.rulenode.attribute-type', - description: 'Fetch attribute value from database' + name: 'rule-node-config.attribute-type', + description: 'rule-node-config.attribute-type-description' } ], [ ArgumentType.TIME_SERIES, { - name: 'tb.rulenode.time-series-type', - description: 'Fetch latest time-series value from database' + name: 'rule-node-config.time-series-type', + description: 'rule-node-config.time-series-type-description' } ], [ ArgumentType.CONSTANT, { - name: 'tb.rulenode.constant-type', - description: 'Define constant value' + name: 'rule-node-config.constant-type', + description: 'rule-node-config.constant-type-description' } ] ]); @@ -786,29 +786,29 @@ export const ArgumentTypeResultMap = new Map([ - [AttributeScope.SHARED_SCOPE, 'tb.rulenode.shared-scope'], - [AttributeScope.SERVER_SCOPE, 'tb.rulenode.server-scope'], - [AttributeScope.CLIENT_SCOPE, 'tb.rulenode.client-scope'] + [AttributeScope.SHARED_SCOPE, 'rule-node-config.shared-scope'], + [AttributeScope.SERVER_SCOPE, 'rule-node-config.server-scope'], + [AttributeScope.CLIENT_SCOPE, 'rule-node-config.client-scope'] ]); export enum PresenceMonitoringStrategy { @@ -847,14 +847,14 @@ export const PresenceMonitoringStrategiesData = new Map
    - tb.rulenode.new-originator + rule-node-config.new-originator @@ -37,7 +37,7 @@
    -
    @@ -49,11 +49,11 @@ class="mat-mdc-form-field flex"> - tb.rulenode.entity-name-pattern + rule-node-config.entity-name-pattern - {{ 'tb.rulenode.entity-name-pattern-required' | translate }} + {{ 'rule-node-config.entity-name-pattern-required' | translate }}
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transformation/copy-keys-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transformation/copy-keys-config.component.html index 85cf878a4b..ccaa44bea1 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/transformation/copy-keys-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/transformation/copy-keys-config.component.html @@ -17,19 +17,19 @@ -->
    + matTooltip="{{ 'rule-node-config.use-regular-expression-hint' | translate }}"> help diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transformation/deduplication-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transformation/deduplication-config.component.html index 5d5ec40580..e85ea71399 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/transformation/deduplication-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/transformation/deduplication-config.component.html @@ -17,34 +17,34 @@ -->
    - {{'tb.rulenode.interval' | translate}} + {{'rule-node-config.interval' | translate}} - {{'tb.rulenode.interval-required' | translate}} + {{'rule-node-config.interval-required' | translate}} - {{'tb.rulenode.interval-min-error' | translate}} + {{'rule-node-config.interval-min-error' | translate}} help + matTooltip="{{ 'rule-node-config.interval-hint' | translate }}">help
    -
    tb.rulenode.strategy
    +
    rule-node-config.strategy
    {{ deduplicationStrategiesTranslations.get(strategy) | translate }} - - - @@ -58,40 +58,40 @@
    - tb.rulenode.advanced-settings + rule-node-config.advanced-settings
    - {{'tb.rulenode.max-pending-msgs' | translate}} + {{'rule-node-config.max-pending-msgs' | translate}} - {{'tb.rulenode.max-pending-msgs-required' | translate}} + {{'rule-node-config.max-pending-msgs-required' | translate}} - {{'tb.rulenode.max-pending-msgs-max-error' | translate}} + {{'rule-node-config.max-pending-msgs-max-error' | translate}} - {{'tb.rulenode.max-pending-msgs-min-error' | translate}} + {{'rule-node-config.max-pending-msgs-min-error' | translate}} help + matTooltip="{{ 'rule-node-config.max-pending-msgs-hint' | translate }}">help - {{'tb.rulenode.max-retries' | translate}} + {{'rule-node-config.max-retries' | translate}} - {{'tb.rulenode.max-retries-required' | translate}} + {{'rule-node-config.max-retries-required' | translate}} - {{'tb.rulenode.max-retries-max-error' | translate}} + {{'rule-node-config.max-retries-max-error' | translate}} - {{'tb.rulenode.max-retries-min-error' | translate}} + {{'rule-node-config.max-retries-min-error' | translate}} help + matTooltip="{{ 'rule-node-config.max-retries-hint' | translate }}">help
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transformation/delete-keys-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transformation/delete-keys-config.component.html index 23737bab9f..2bab63f4d6 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/transformation/delete-keys-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/transformation/delete-keys-config.component.html @@ -17,18 +17,18 @@ -->
    + matTooltip="{{ 'rule-node-config.use-regular-expression-delete-hint' | translate }}"> help diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transformation/node-json-path-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transformation/node-json-path-config.component.html index c1d3c8e2e4..94f8046b1b 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/transformation/node-json-path-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/transformation/node-json-path-config.component.html @@ -17,9 +17,9 @@ -->
    - {{ 'tb.rulenode.json-path-expression' | translate }} + {{ 'rule-node-config.json-path-expression' | translate }} - {{ 'tb.rulenode.json-path-expression-hint' | translate }} - {{ 'tb.rulenode.json-path-expression-required' | translate }} + {{ 'rule-node-config.json-path-expression-hint' | translate }} + {{ 'rule-node-config.json-path-expression-required' | translate }}
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transformation/rename-keys-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transformation/rename-keys-config.component.html index 3099a31e4e..7543875f19 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/transformation/rename-keys-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/transformation/rename-keys-config.component.html @@ -16,7 +16,7 @@ -->
    -
    tb.rulenode.rename-keys-in
    +
    rule-node-config.rename-keys-in
    @@ -28,13 +28,13 @@
    + requiredText="{{'rule-node-config.attr-mapping-required' | translate}}" + keyText="{{'rule-node-config.current-key-name' | translate}}" + keyRequiredText="{{'rule-node-config.key-name-required' | translate}}" + valText="{{'rule-node-config.new-key-name' | translate}}" + valRequiredText="{{'rule-node-config.new-key-name-required' | translate}}">
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transformation/script-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/transformation/script-config.component.ts index 7e462ee039..848a9dd2e9 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/transformation/script-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/transformation/script-config.component.ts @@ -46,7 +46,7 @@ export class TransformScriptConfigComponent extends RuleNodeConfigurationCompone readonly hasScript = true; - readonly testScriptLabel = 'tb.rulenode.test-transformer-function'; + readonly testScriptLabel = 'rule-node-config.test-transformer-function'; constructor(private fb: FormBuilder, private nodeScriptTestService: NodeScriptTestService, @@ -104,7 +104,7 @@ export class TransformScriptConfigComponent extends RuleNodeConfigurationCompone this.nodeScriptTestService.testNodeScript( script, 'update', - this.translate.instant('tb.rulenode.transformer'), + this.translate.instant('rule-node-config.transformer'), 'Transform', ['msg', 'metadata', 'msgType'], this.ruleNodeId, diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transformation/to-email-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transformation/to-email-config.component.html index 87106d836d..373a4c6b57 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/transformation/to-email-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/transformation/to-email-config.component.html @@ -17,13 +17,13 @@ -->
    -
    tb.rulenode.email-sender
    +
    rule-node-config.email-sender
    - tb.rulenode.from-template + rule-node-config.from-template - {{ 'tb.rulenode.email-from-template-hint' | translate }} + {{ 'rule-node-config.email-from-template-hint' | translate }}
    @@ -33,25 +33,25 @@ tb-help-popup-placement="right" trigger-style="letter-spacing:0.25px; font-size:12px;" [tb-help-popup-style]="{maxWidth: '820px'}" - trigger-text="{{ 'tb.key-val.see-examples' | translate }}">
    + trigger-text="{{ 'rule-node-config.key-val.see-examples' | translate }}">
    - {{ 'tb.rulenode.from-template-required' | translate }} + {{ 'rule-node-config.from-template-required' | translate }}
    -
    tb.rulenode.recipients
    - rule-node-config.recipients
    +
    - tb.rulenode.to-template + rule-node-config.to-template - {{ 'tb.rulenode.to-template-required' | translate }} + {{ 'rule-node-config.to-template-required' | translate }} - tb.rulenode.cc-template + rule-node-config.cc-template - tb.rulenode.bcc-template + rule-node-config.bcc-template - {{ 'tb.rulenode.subject-template-required' | translate }} + {{ 'rule-node-config.subject-template-required' | translate }} - tb.rulenode.mail-body-type + rule-node-config.mail-body-type @@ -118,12 +118,12 @@ - tb.rulenode.body-type-template + rule-node-config.body-type-template - tb.mail-body-type.after-template-evaluation-hint + rule-node-config.mail-body-types.after-template-evaluation-hint - tb.rulenode.body-template + rule-node-config.body-template - {{ 'tb.rulenode.body-template-required' | translate }} + {{ 'rule-node-config.body-template-required' | translate }}
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transformation/to-email-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/transformation/to-email-config.component.ts index 3c922a7067..97282025fd 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/transformation/to-email-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/transformation/to-email-config.component.ts @@ -29,18 +29,18 @@ export class ToEmailConfigComponent extends RuleNodeConfigurationComponent { toEmailConfigForm: FormGroup; mailBodyTypes = [ { - name: 'tb.mail-body-type.plain-text', - description: 'tb.mail-body-type.plain-text-description', + name: 'rule-node-config.mail-body-types.plain-text', + description: 'rule-node-config.mail-body-types.plain-text-description', value: 'false', }, { - name: 'tb.mail-body-type.html', - description: 'tb.mail-body-type.html-text-description', + name: 'rule-node-config.mail-body-types.html', + description: 'rule-node-config.mail-body-types.html-text-description', value: 'true', }, { - name: 'tb.mail-body-type.use-body-type-template', - description: 'tb.mail-body-type.dynamic-text-description', + name: 'rule-node-config.mail-body-types.use-body-type-template', + description: 'rule-node-config.mail-body-types.dynamic-text-description', value: 'dynamic', } ]; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index cbae98683c..2fb67adbba 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -4359,6 +4359,741 @@ "queue-hint": "Select a queue for message forwarding to another queue. 'Main' queue is used by default.", "queue-singleton-hint": "Select a queue for message forwarding in multi-instance environments. 'Main' queue is used by default." }, + "rule-node-config": { + "id": "Id", + "additional-info": "Additional Info", + "advanced-settings": "Advanced settings", + "create-entity-if-not-exists": "Create new entity if it doesn't exist", + "create-entity-if-not-exists-hint": "If enabled, a new entity with specified parameters will be created unless it already exists. Existing entities will be used as is for relation.", + "select-device-connectivity-event": "Select device connectivity event", + "entity-name-pattern": "Name pattern", + "device-name-pattern": "Device name", + "asset-name-pattern": "Asset name", + "entity-view-name-pattern": "Entity view name", + "customer-title-pattern": "Customer title", + "dashboard-name-pattern": "Dashboard title", + "user-name-pattern": "User email", + "edge-name-pattern": "Edge name", + "entity-name-pattern-required": "Name pattern is required", + "entity-name-pattern-hint": "Name pattern field support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", + "copy-message-type": "Copy message type", + "entity-type-pattern": "Type pattern", + "entity-type-pattern-required": "Type pattern is required", + "message-type-value": "Message type value", + "message-type-value-required": "Message type value is required", + "message-type-value-max-length": "Message type value should be less than 256", + "output-message-type": "Output message type", + "entity-cache-expiration": "Entities cache expiration time (sec)", + "entity-cache-expiration-hint": "Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.", + "entity-cache-expiration-required": "Entities cache expiration time is required.", + "entity-cache-expiration-range": "Entities cache expiration time should be greater than or equal to 0.", + "customer-name-pattern": "Customer title", + "customer-name-pattern-required": "Customer title is required", + "customer-name-pattern-hint": "Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", + "create-customer-if-not-exists": "Create new customer if it doesn't exist", + "unassign-from-customer": "Unassign from specific customer if originator is dashboard", + "unassign-from-customer-tooltip": "Only dashboards can be assigned to multiple customers at once. \nIf the message originator is a dashboard, you need to explicitly specify the customer's title to unassign from.", + "customer-cache-expiration": "Customers cache expiration time (sec)", + "customer-cache-expiration-hint": "Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.", + "customer-cache-expiration-required": "Customers cache expiration time is required.", + "customer-cache-expiration-range": "Customers cache expiration time should be greater than or equal to 0.", + "interval-start": "Interval start", + "interval-end": "Interval end", + "time-unit": "Time unit", + "fetch-mode": "Fetch mode", + "order-by-timestamp": "Order by timestamp", + "limit": "Limit", + "limit-hint": "Min limit value is 2, max - 1000. If you want to fetch a single entry, select fetch mode 'First' or 'Last'.", + "limit-required": "Limit is required.", + "limit-range": "Limit should be in a range from 2 to 1000.", + "time-unit-milliseconds": "Milliseconds", + "time-unit-seconds": "Seconds", + "time-unit-minutes": "Minutes", + "time-unit-hours": "Hours", + "time-unit-days": "Days", + "time-value-range": "Allowing range from 1 to 2147483647.", + "start-interval-value-required": "Interval start is required.", + "end-interval-value-required": "Interval end is required.", + "filter": "Filter", + "switch": "Switch", + "math-templatization-tooltip": "This field support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", + "add-message-type": "Add message type", + "select-message-types-required": "At least one message type should be selected.", + "select-message-types": "Select message types", + "no-message-types-found": "No message types found", + "no-message-type-matching": "'{{messageType}}' not found.", + "create-new-message-type": "Create a new one.", + "message-types-required": "Message types are required.", + "client-attributes": "Client attributes", + "shared-attributes": "Shared attributes", + "server-attributes": "Server attributes", + "attributes-keys": "Attributes keys", + "attributes-keys-required": "Attributes keys are required", + "attributes-scope": "Attributes scope", + "attributes-scope-value": "Attributes scope value", + "attributes-scope-value-copy": "Copy attributes scope value", + "attributes-scope-hint": "Use the 'scope' metadata key to dynamically set the attribute scope per message. If provided, this overrides the scope set in the configuration.", + "notify-device": "Force notification to the device", + "send-attributes-updated-notification": "Send attributes updated notification", + "send-attributes-updated-notification-hint": "Send notification about updated attributes as a separate message to the rule engine queue.", + "send-attributes-deleted-notification": "Send attributes deleted notification", + "send-attributes-deleted-notification-hint": "Send notification about deleted attributes as a separate message to the rule engine queue.", + "update-attributes-only-on-value-change": "Save attributes only if the value changes", + "update-attributes-only-on-value-change-hint": "Updates the attributes on every incoming message disregarding if their value has changed. Increases API usage and reduces performance.", + "update-attributes-only-on-value-change-hint-enabled": "Updates the attributes only if their value has changed. If the value is not changed, no update to the attribute timestamp nor attribute change notification will be sent.", + "fetch-credentials-to-metadata": "Fetch credentials to metadata", + "notify-device-on-update-hint": "If enabled, force notification to the device about shared attributes update. If disabled, the notification behavior is controlled by the 'notifyDevice' parameter from the incoming message metadata. To turn off the notification, the message metadata must contain the 'notifyDevice' parameter set to 'false'. Any other case will trigger the notification to the device.", + "notify-device-on-delete-hint": "If enabled, force notification to the device about shared attributes removal. If disabled, the notification behavior is controlled by the 'notifyDevice' parameter from the incoming message metadata. To turn on the notification, the message metadata must contain the 'notifyDevice' parameter set to 'true'. In any other case, the notification will not be triggered to the device.", + "latest-timeseries": "Latest time series data keys", + "timeseries-keys": "Time series keys", + "timeseries-keys-required": "At least one time series key should be selected.", + "add-timeseries-key": "Add time series key", + "add-message-field": "Add message field", + "relation-search-parameters": "Relation search parameters", + "relation-parameters": "Relation parameters", + "add-metadata-field": "Add metadata field", + "data-keys": "Message field names", + "copy-from": "Copy from", + "data-to-metadata": "Data to metadata", + "metadata-to-data": "Metadata to data", + "use-regular-expression-hint": "Use regular expression to copy keys by pattern.\n\nTips & tricks:\nPress 'Enter' to complete field name input.\nPress 'Backspace' to delete field name. Multiple field names supported.", + "interval": "Interval", + "interval-required": "Interval is required", + "interval-hint": "Deduplication interval in seconds.", + "interval-min-error": "Min allowed value is 1", + "max-pending-msgs": "Max pending messages", + "max-pending-msgs-hint": "Maximum number of messages that are stored in memory for each unique deduplication id.", + "max-pending-msgs-required": "Max pending messages is required", + "max-pending-msgs-max-error": "Max allowed value is 1000", + "max-pending-msgs-min-error": "Min allowed value is 1", + "max-retries": "Max retries", + "max-retries-required": "Max retries is required", + "max-retries-hint": "Maximum number of retries to push the deduplicated messages into the queue. 10 seconds delay is used between retries", + "max-retries-max-error": "Max allowed value is 100", + "max-retries-min-error": "Min allowed value is 0", + "strategy": "Strategy", + "strategy-required": "Strategy is required", + "strategy-all-hint": "Return all messages that arrived during deduplication period as a single JSON array message. Where each element represents object with msg and metadata inner properties.", + "strategy-first-hint": "Return first message that arrived during deduplication period.", + "strategy-last-hint": "Return last message that arrived during deduplication period.", + "first": "First", + "last": "Last", + "all": "All", + "output-msg-type-hint": "The message type of the deduplication result.", + "queue-name-hint": "The queue name where the deduplication result will be published.", + "keys": "Keys", + "keys-required": "Keys are required", + "rename-keys-in": "Rename keys in", + "data": "Data", + "message": "Message", + "metadata": "Metadata", + "current-key-name": "Current key name", + "key-name-required": "Key name is required", + "new-key-name": "New key name", + "new-key-name-required": "New key name is required", + "metadata-keys": "Metadata field names", + "json-path-expression": "JSON path expression", + "json-path-expression-required": "JSON path expression is required", + "json-path-expression-hint": "JSONPath specifies a path to an element or a set of elements in a JSON structure. '$' represents the root object or array.", + "relations-query": "Relations query", + "device-relations-query": "Device relations query", + "max-relation-level": "Max relation level", + "max-relation-level-error": "Value should be greater than 0 or unspecified.", + "max-relation-level-invalid": "Value should be an integer.", + "relation-type": "Relation type", + "relation-type-pattern": "Relation type pattern", + "relation-type-pattern-required": "Relation type pattern is required", + "relation-types-list": "Relation types to propagate", + "relation-types-list-hint": "If Propagate relation types are not selected, alarms will be propagated without filtering by relation type.", + "unlimited-level": "Unlimited level", + "latest-telemetry": "Latest telemetry", + "add-telemetry-key": "Add telemetry key", + "delete-from": "Delete from", + "use-regular-expression-delete-hint": "Use regular expression to delete keys by pattern.\n\nTips & tricks:\nPress 'Enter' to complete field name input.\nPress 'Backspace' to delete field name.\nMultiple field names supported.", + "fetch-into": "Fetch into", + "attr-mapping": "Attributes mapping:", + "source-attribute": "Source attribute key", + "source-attribute-required": "Source attribute key is required.", + "source-telemetry": "Source telemetry key", + "source-telemetry-required": "Source telemetry key is required.", + "target-key": "Target key", + "target-key-required": "Target key is required.", + "attr-mapping-required": "At least one mapping entry should be specified.", + "fields-mapping": "Fields mapping", + "fields-mapping-hint": "If the message field is set to $entityId, the message originator's id will be saved to the specified table column.", + "relations-query-config-direction-suffix": "originator", + "profile-name": "Profile name", + "fetch-circle-parameter-info-from-metadata-hint": "Metadata field '{{perimeterKeyName}}' should be defined in next format: {\"latitude\":48.196, \"longitude\":24.6532, \"radius\":100.0, \"radiusUnit\":\"METER\"}", + "fetch-poligon-parameter-info-from-metadata-hint": "Metadata field '{{perimeterKeyName}}' should be defined in next format: [[48.19736,24.65235],[48.19800,24.65060],...,[48.19849,24.65420]]", + "short-templatization-tooltip": "Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", + "fields-mapping-required": "At least one field mapping should be specified.", + "at-least-one-field-required": "At least one input field must have a value(s) provided.", + "originator-fields-sv-map-hint": "Target key fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", + "sv-map-hint": "Only target key fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", + "source-field": "Source field", + "source-field-required": "Source field is required.", + "originator-source": "Originator source", + "new-originator": "New originator", + "originator-customer": "Customer", + "originator-tenant": "Tenant", + "originator-related": "Related entity", + "originator-alarm-originator": "Alarm Originator", + "originator-entity": "Entity by name pattern", + "clone-message": "Clone message", + "transform": "Transform", + "default-ttl": "Default TTL in seconds", + "default-ttl-required": "Default TTL is required.", + "default-ttl-hint": "Rule node will fetch Time-to-Live (TTL) value from the message metadata. If no value is present, it defaults to the TTL specified in the configuration. If the value is set to 0, the TTL from the tenant profile configuration will be applied.", + "default-ttl-zero-hint": "TTL will not be applied if its value is set to 0.", + "min-default-ttl-message": "Only 0 minimum TTL is allowed.", + "generation-parameters": "Generation parameters", + "message-count": "Generated messages limit (0 - unlimited)", + "message-count-required": "Generated messages limit is required.", + "min-message-count-message": "Only 0 minimum message count is allowed.", + "period-seconds": "Period in seconds", + "period-seconds-required": "Period is required.", + "generation-frequency-seconds": "Generation frequency in seconds", + "generation-frequency-required": "Generation frequency is required.", + "min-generation-frequency-message": "Only 1 second minimum is allowed.", + "script-lang-tbel": "TBEL", + "script-lang-js": "JS", + "use-metadata-period-in-seconds-patterns": "Use period in seconds pattern", + "use-metadata-period-in-seconds-patterns-hint": "If selected, rule node use period in seconds interval pattern from message metadata or data assuming that intervals are in the seconds.", + "period-in-seconds-pattern": "Period in seconds pattern", + "period-in-seconds-pattern-required": "Period in seconds pattern is required", + "min-period-seconds-message": "Only 1 second minimum period is allowed.", + "originator": "Originator", + "message-body": "Message body", + "message-metadata": "Message metadata", + "generate": "Generate", + "current-rule-node": "Current Rule Node", + "current-tenant": "Current Tenant", + "generator-function": "Generator function", + "test-generator-function": "Test generator function", + "generator": "Generator", + "test-filter-function": "Test filter function", + "test-switch-function": "Test switch function", + "test-transformer-function": "Test transformer function", + "transformer": "Transformer", + "alarm-create-condition": "Alarm create condition", + "test-condition-function": "Test condition function", + "alarm-clear-condition": "Alarm clear condition", + "alarm-details-builder": "Alarm details builder", + "test-details-function": "Test details function", + "alarm-type": "Alarm type", + "select-entity-types": "Select entity types", + "alarm-type-required": "Alarm type is required.", + "alarm-severity": "Alarm severity", + "alarm-severity-required": "Alarm severity is required", + "alarm-severity-pattern": "Alarm severity pattern", + "alarm-status-filter": "Alarm status filter", + "alarm-status-list-empty": "Alarm status list is empty", + "no-alarm-status-matching": "No alarm status matching were found.", + "propagate": "Propagate alarm to related entities", + "propagate-to-owner": "Propagate alarm to entity owner (Customer or Tenant)", + "propagate-to-tenant": "Propagate alarm to Tenant", + "condition": "Condition", + "details": "Details", + "to-string": "To string", + "test-to-string-function": "Test to string function", + "from-template": "From", + "from-template-required": "From is required", + "message-to-metadata": "Message to metadata", + "metadata-to-message": "Metadata to message", + "from-message": "From message", + "from-metadata": "From metadata", + "to-template": "To", + "to-template-required": "To Template is required", + "mail-address-list-template-hint": "Comma separated address list, use ${metadataKey} for value from metadata, $[messageKey] for value from message body", + "cc-template": "Cc", + "bcc-template": "Bcc", + "subject-template": "Subject", + "subject-template-required": "Subject Template is required", + "body-template": "Body", + "body-template-required": "Body Template is required", + "dynamic-mail-body-type": "Dynamic mail body type", + "mail-body-type": "Mail body type", + "body-type-template": "Body type template", + "reply-routing-configuration": "Reply Routing Configuration", + "rpc-reply-routing-configuration-hint": "These configuration parameters specify the metadata key names used to identify the service, session, and request for sending a reply back.", + "reply-routing-configuration-hint": "These configuration parameters specify the metadata key names used to identify the service and request for sending a reply back.", + "request-id-metadata-attribute": "Request Id", + "service-id-metadata-attribute": "Service Id", + "session-id-metadata-attribute": "Session Id", + "timeout-sec": "Timeout in seconds", + "timeout-required": "Timeout is required", + "min-timeout-message": "Only 0 minimum timeout value is allowed.", + "endpoint-url-pattern": "Endpoint URL pattern", + "endpoint-url-pattern-required": "Endpoint URL pattern is required", + "request-method": "Request method", + "use-simple-client-http-factory": "Use simple client HTTP factory", + "ignore-request-body": "Without request body", + "parse-to-plain-text": "Parse to plain text", + "parse-to-plain-text-hint": "If selected, request body message payload will be transformed from JSON string to plain text, e.g. msg = \"Hello,\\t\"world\"\" will be parsed to Hello, \"world\"", + "read-timeout": "Read timeout in millis", + "read-timeout-hint": "The value of 0 means an infinite timeout", + "max-parallel-requests-count": "Max number of parallel requests", + "max-parallel-requests-count-hint": "The value of 0 specifies no limit in parallel processing", + "max-response-size": "Max response size (in KB)", + "max-response-size-hint": "The maximum amount of memory allocated for buffering data when decoding or encoding HTTP messages, such as JSON or XML payloads", + "headers": "Headers", + "headers-hint": "Use ${metadataKey} for value from metadata, $[messageKey] for value from message body in header/value fields", + "header": "Header", + "header-required": "Header is required", + "value": "Value", + "value-required": "Value is required", + "topic-pattern": "Topic pattern", + "key-pattern": "Key pattern", + "key-pattern-hint": "Optional. If a valid partition number is specified, it will be used when sending the record. If no partition is specified, the key will be used instead. If neither is specified, a partition will be assigned in a round-robin fashion.", + "topic-pattern-required": "Topic pattern is required", + "topic": "Topic", + "topic-required": "Topic is required", + "bootstrap-servers": "Bootstrap servers", + "bootstrap-servers-required": "Bootstrap servers value is required", + "other-properties": "Other properties", + "key": "Key", + "key-required": "Key is required", + "retries": "Automatically retry times if fails", + "min-retries-message": "Only 0 minimum retries is allowed.", + "batch-size-bytes": "Produces batch size in bytes", + "min-batch-size-bytes-message": "Only 0 minimum batch size is allowed.", + "linger-ms": "Time to buffer locally (ms)", + "min-linger-ms-message": "Only 0 ms minimum value is allowed.", + "buffer-memory-bytes": "Client buffer max size in bytes", + "min-buffer-memory-message": "Only 0 minimum buffer size is allowed.", + "memory-buffer-size-range": "Memory buffer size must be between 0 and {{max}} KB", + "acks": "Number of acknowledgments", + "key-serializer": "Key serializer", + "key-serializer-required": "Key serializer is required", + "value-serializer": "Value serializer", + "value-serializer-required": "Value serializer is required", + "topic-arn-pattern": "Topic ARN pattern", + "topic-arn-pattern-required": "Topic ARN pattern is required", + "aws-access-key-id": "AWS Access Key ID", + "aws-access-key-id-required": "AWS Access Key ID is required", + "aws-secret-access-key": "AWS Secret Access Key", + "aws-secret-access-key-required": "AWS Secret Access Key is required", + "aws-region": "AWS Region", + "aws-region-required": "AWS Region is required", + "exchange-name-pattern": "Exchange name pattern", + "routing-key-pattern": "Routing key pattern", + "message-properties": "Message properties", + "host": "Host", + "host-required": "Host is required", + "port": "Port", + "port-required": "Port is required", + "port-range": "Port should be in a range from 1 to 65535.", + "virtual-host": "Virtual host", + "username": "Username", + "password": "Password", + "automatic-recovery": "Automatic recovery", + "connection-timeout-ms": "Connection timeout (ms)", + "min-connection-timeout-ms-message": "Only 0 ms minimum value is allowed.", + "handshake-timeout-ms": "Handshake timeout (ms)", + "min-handshake-timeout-ms-message": "Only 0 ms minimum value is allowed.", + "client-properties": "Client properties", + "queue-url-pattern": "Queue URL pattern", + "queue-url-pattern-required": "Queue URL pattern is required", + "delay-seconds": "Delay (seconds)", + "min-delay-seconds-message": "Only 0 seconds minimum value is allowed.", + "max-delay-seconds-message": "Only 900 seconds maximum value is allowed.", + "name": "Name", + "name-required": "Name is required", + "queue-type": "Queue type", + "sqs-queue-standard": "Standard", + "sqs-queue-fifo": "FIFO", + "gcp-project-id": "GCP project ID", + "gcp-project-id-required": "GCP project ID is required", + "gcp-service-account-key": "GCP service account key file", + "gcp-service-account-key-required": "GCP service account key file is required", + "pubsub-topic-name": "Topic name", + "pubsub-topic-name-required": "Topic name is required", + "message-attributes": "Message attributes", + "message-attributes-hint": "Use ${metadataKey} for value from metadata, $[messageKey] for value from message body in name/value fields", + "connect-timeout": "Connection timeout (sec)", + "connect-timeout-required": "Connection timeout is required.", + "connect-timeout-range": "Connection timeout should be in a range from 1 to 200.", + "client-id": "Client ID", + "client-id-hint": "Optional. Leave empty for auto-generated Client ID. Be careful when specifying the Client ID. Majority of the MQTT brokers will not allow multiple connections with the same Client ID. To connect to such brokers, your mqtt Client ID must be unique. When platform is running in a micro-services mode, the copy of rule node is launched in each micro-service. This will automatically lead to multiple mqtt clients with the same ID and may cause failures of the rule node. To avoid such failures enable \"Add Service ID as suffix to Client ID\" option below.", + "append-client-id-suffix": "Add Service ID as suffix to Client ID", + "client-id-suffix-hint": "Optional. Applied when \"Client ID\" specified explicitly. If selected then Service ID will be added to Client ID as a suffix. Helps to avoid failures when platform is running in a micro-services mode.", + "device-id": "Device ID", + "device-id-required": "Device ID is required.", + "clean-session": "Clean session", + "enable-ssl": "Enable SSL", + "credentials": "Credentials", + "credentials-type": "Credentials type", + "credentials-type-required": "Credentials type is required.", + "credentials-anonymous": "Anonymous", + "credentials-basic": "Basic", + "credentials-pem": "PEM", + "credentials-pem-hint": "At least Server CA certificate file or a pair of Client certificate and Client private key files are required", + "credentials-sas": "Shared Access Signature", + "sas-key": "SAS Key", + "sas-key-required": "SAS Key is required.", + "hostname": "Hostname", + "hostname-required": "Hostname is required.", + "azure-ca-cert": "CA certificate file", + "username-required": "Username is required.", + "password-required": "Password is required.", + "ca-cert": "Server CA certificate file", + "private-key": "Client private key file", + "cert": "Client certificate file", + "no-file": "No file selected.", + "drop-file": "Drop a file or click to select a file to upload.", + "private-key-password": "Private key password", + "use-system-smtp-settings": "Use system SMTP settings", + "use-metadata-dynamic-interval": "Use dynamic interval", + "metadata-dynamic-interval-hint": "Interval start and end input fields support templatization. Note that the substituted template value should be set in milliseconds. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", + "use-metadata-interval-patterns-hint": "If selected, rule node use start and end interval patterns from message metadata or data assuming that intervals are in the milliseconds.", + "use-message-alarm-data": "Use message alarm data", + "overwrite-alarm-details": "Overwrite alarm details", + "use-alarm-severity-pattern": "Use alarm severity pattern", + "check-all-keys": "Check that all specified fields are present", + "check-all-keys-hint": "If selected, checks that all specified keys are present in the message data and metadata.", + "check-relation-to-specific-entity": "Check relation to specific entity", + "check-relation-to-specific-entity-tooltip": "If enabled, checks the presence of relation with a specific entity otherwise, checks the presence of relation with any entity. In both cases, relation lookup is based on configured direction and type.", + "check-relation-hint": "Checks existence of relation to specific entity or to any entity based on direction and relation type.", + "delete-relation-with-specific-entity": "Delete relation with specific entity", + "delete-relation-with-specific-entity-hint": "If enabled, will delete the relation with just one specific entity. Otherwise, the relation will be removed with all matching entities.", + "delete-relation-hint": "Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.", + "remove-current-relations": "Remove current relations", + "remove-current-relations-hint": "Removes current relations from the originator of the incoming message based on direction and type.", + "change-originator-to-related-entity": "Change originator to related entity", + "change-originator-to-related-entity-hint": "Used to process submitted message as a message from another entity.", + "start-interval": "Interval start", + "end-interval": "Interval end", + "start-interval-required": "Interval start is required.", + "end-interval-required": "Interval end is required.", + "smtp-protocol": "Protocol", + "smtp-host": "SMTP host", + "smtp-host-required": "SMTP host is required.", + "smtp-port": "SMTP port", + "smtp-port-required": "You must supply a smtp port.", + "smtp-port-range": "SMTP port should be in a range from 1 to 65535.", + "timeout-msec": "Timeout ms", + "min-timeout-msec-message": "Only 0 ms minimum value is allowed.", + "enter-username": "Enter username", + "enter-password": "Enter password", + "enable-tls": "Enable TLS", + "tls-version": "TLS version", + "enable-proxy": "Enable proxy", + "use-system-proxy-properties": "Use system proxy properties", + "proxy-host": "Proxy host", + "proxy-host-required": "Proxy host is required.", + "proxy-port": "Proxy port", + "proxy-port-required": "Proxy port is required.", + "proxy-port-range": "Proxy port should be in a range from 1 to 65535.", + "proxy-user": "Proxy user", + "proxy-password": "Proxy password", + "proxy-scheme": "Proxy scheme", + "numbers-to-template": "Phone Numbers To Template", + "numbers-to-template-required": "Phone Numbers To Template is required", + "numbers-to-template-hint": "Comma separated Phone Numbers, use ${metadataKey} for value from metadata, $[messageKey] for value from message body", + "sms-message-template": "SMS message Template", + "sms-message-template-required": "SMS message Template is required", + "use-system-sms-settings": "Use system SMS provider settings", + "min-period-0-seconds-message": "Only 0 second minimum period is allowed.", + "max-pending-messages": "Maximum pending messages", + "max-pending-messages-required": "Maximum pending messages is required.", + "max-pending-messages-range": "Maximum pending messages should be in a range from 1 to 100000.", + "originator-types-filter": "Originator types filter", + "interval-seconds": "Interval in seconds", + "interval-seconds-required": "Interval is required.", + "int-range": "Value must not exceed the maximum integer limit (2147483648)", + "min-interval-seconds-message": "Only 1 second minimum interval is allowed.", + "output-timeseries-key-prefix": "Output time series key prefix", + "output-timeseries-key-prefix-required": "Output time series key prefix required.", + "separator-hint": "You should press \"Enter\" to complete field input.", + "select-details": "Select details", + "entity-details-id": "Id", + "entity-details-title": "Title", + "entity-details-country": "Country", + "entity-details-state": "State", + "entity-details-city": "City", + "entity-details-zip": "Zip", + "entity-details-address": "Address", + "entity-details-address2": "Address2", + "entity-details-additional_info": "Additional Info", + "entity-details-phone": "Phone", + "entity-details-email": "Email", + "email-sender": "Email sender", + "fields-to-check": "Fields to check", + "add-detail": "Add detail", + "check-all-keys-tooltip": "If enabled, checks the presence of all fields listed in the message and metadata field names within the incoming message and its metadata.", + "fields-to-check-hint": "Press \"Enter\" to complete field name input. Multiple field names supported.", + "entity-details-list-empty": "At least one detail should be selected.", + "alarm-status": "Alarm status", + "alarm-required": "At least one alarm status should be selected.", + "no-entity-details-matching": "No entity details matching were found.", + "custom-table-name": "Custom table name", + "custom-table-name-required": "Table Name is required", + "custom-table-hint": "The table must be created in your Cassandra cluster and its name must start with the prefix 'cs_tb_' to avoid the data insertion to the common TB tables. Enter the table name here without the 'cs_tb_' prefix.", + "message-field": "Message field", + "message-field-required": "Message field is required.", + "table-col": "Table column", + "table-col-required": "Table column is required.", + "latitude-field-name": "Latitude field name", + "longitude-field-name": "Longitude field name", + "latitude-field-name-required": "Latitude field name is required.", + "longitude-field-name-required": "Longitude field name is required.", + "fetch-perimeter-info-from-metadata": "Fetch perimeter information from metadata", + "fetch-perimeter-info-from-metadata-tooltip": "If perimeter type is set to 'Polygon' the value of metadata field '{{perimeterKeyName}}' will be set as perimeter definition without additional parsing of the value. Otherwise, if perimeter type is set to 'Circle' the value of '{{perimeterKeyName}}' metadata field will be parsed to extract 'latitude', 'longitude', 'radius', 'radiusUnit' fields that uses for circle perimeter definition.", + "perimeter-key-name": "Perimeter key name", + "perimeter-key-name-hint": "Metadata field name that includes perimeter information.", + "perimeter-key-name-required": "Perimeter key name is required.", + "perimeter-circle": "Circle", + "perimeter-polygon": "Polygon", + "perimeter-type": "Perimeter type", + "circle-center-latitude": "Center latitude", + "circle-center-latitude-required": "Center latitude is required.", + "circle-center-longitude": "Center longitude", + "circle-center-longitude-required": "Center longitude is required.", + "range-unit-meter": "Meter", + "range-unit-kilometer": "Kilometer", + "range-unit-foot": "Foot", + "range-unit-mile": "Mile", + "range-unit-nautical-mile": "Nautical mile", + "range-units": "Range units", + "range-units-required": "Range units is required.", + "range": "Range", + "range-required": "Range is required.", + "polygon-definition": "Polygon definition", + "polygon-definition-required": "Polygon definition is required.", + "polygon-definition-hint": "Use the following format for manual definition of polygon: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].", + "min-inside-duration": "Minimal inside duration", + "min-inside-duration-value-required": "Minimal inside duration is required", + "min-inside-duration-time-unit": "Minimal inside duration time unit", + "min-outside-duration": "Minimal outside duration", + "min-outside-duration-value-required": "Minimal outside duration is required", + "min-outside-duration-time-unit": "Minimal outside duration time unit", + "tell-failure-if-absent": "Tell Failure", + "tell-failure-if-absent-hint": "If at least one selected key doesn't exist the outbound message will report \"Failure\".", + "get-latest-value-with-ts": "Fetch timestamp for the latest telemetry values", + "get-latest-value-with-ts-hint": "If selected, the latest telemetry values will also include timestamp, e.g: \"temp\": \"{\"ts\":1574329385897, \"value\":42}\"", + "ignore-null-strings": "Ignore null strings", + "ignore-null-strings-hint": "If selected rule node will ignore entity fields with empty value.", + "add-metadata-key-values-as-kafka-headers": "Add Message metadata key-value pairs to Kafka record headers", + "add-metadata-key-values-as-kafka-headers-hint": "If selected, key-value pairs from message metadata will be added to the outgoing records headers as byte arrays with predefined charset encoding.", + "charset-encoding": "Charset encoding", + "charset-encoding-required": "Charset encoding is required.", + "charset-us-ascii": "US-ASCII", + "charset-iso-8859-1": "ISO-8859-1", + "charset-utf-8": "UTF-8", + "charset-utf-16be": "UTF-16BE", + "charset-utf-16le": "UTF-16LE", + "charset-utf-16": "UTF-16", + "select-queue-hint": "The queue name can be selected from a drop-down list or add a custom name.", + "device-profile-node-hint": "Useful if you have duration or repeating conditions to ensure continuity of alarm state evaluation.", + "persist-alarm-rules": "Persist state of alarm rules", + "persist-alarm-rules-hint": "If enabled, the rule node will store the state of processing to the database.", + "fetch-alarm-rules": "Fetch state of alarm rules", + "fetch-alarm-rules-hint": "If enabled, the rule node will restore the state of processing on initialization and ensure that alarms are raised even after server restarts. Otherwise, the state will be restored when the first message from the device arrives.", + "input-value-key": "Input value key", + "input-value-key-required": "Input value key is required.", + "output-value-key": "Output value key", + "output-value-key-required": "Output value key is required.", + "number-of-digits-after-floating-point": "Number of digits after floating point", + "number-of-digits-after-floating-point-range": "Number of digits after floating point should be in a range from 0 to 15.", + "failure-if-delta-negative": "Tell Failure if delta is negative", + "failure-if-delta-negative-tooltip": "Rule node forces failure of message processing if delta value is negative.", + "use-caching": "Use caching", + "use-caching-tooltip": "Rule node will cache the value of \"{{inputValueKey}}\" that arrives from the incoming message to improve performance. Note that the cache will not be updated if you modify the \"{{inputValueKey}}\" value elsewhere.", + "add-time-difference-between-readings": "Add the time difference between \"{{inputValueKey}}\" readings", + "add-time-difference-between-readings-tooltip": "If enabled, the rule node will add the \"{{periodValueKey}}\" to the outbound message.", + "period-value-key": "Period value key", + "period-value-key-required": "Period value key is required.", + "general-pattern-hint": "Use ${metadataKey} for value from metadata, $[messageKey] for value from message body.", + "alarm-severity-pattern-hint": "Use ${metadataKey} for value from metadata, $[messageKey] for value from message body. Alarm severity should be system (CRITICAL, MAJOR etc.)", + "output-node-name-hint": "The 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.", + "skip-latest-persistence": "Skip latest persistence", + "skip-latest-persistence-hint": "Rule node will not update values for incoming keys for the latest time series data. Useful for highly loaded use-cases to decrease the pressure on the DB.", + "use-server-ts": "Use server ts", + "use-server-ts-hint": "Rule node will use the timestamp of message processing instead of the timestamp from the message. Useful for all sorts of sequential processing if you merge messages from multiple sources (devices, assets, etc).", + "kv-map-pattern-hint": "All input fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", + "kv-map-single-pattern-hint": "Input field support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", + "shared-scope": "Shared scope", + "server-scope": "Server scope", + "client-scope": "Client scope", + "attribute-type": "Attribute", + "attribute-type-description": "Fetch attribute value from database", + "attribute-type-result-description": "Store result as an entity attribute in the database", + "constant-type": "Constant", + "constant-type-description": "Define constant value", + "time-series-type": "Time series", + "time-series-type-description": "Fetch latest time-series value from database", + "time-series-type-result-description": "Store result as an entity time-series in the database", + "message-body-type": "Message", + "message-body-type-description": "Fetch argument value from incoming message", + "message-body-type-result-description": "Add result to the outgoing message", + "message-metadata-type": "Metadata", + "message-metadata-type-description": "Fetch argument value from incoming message metadata", + "message-metadata-result-description": "Add result to the outgoing message metadata", + "argument-tile": "Arguments", + "no-arguments-prompt": "No arguments configured", + "result-title": "Result", + "functions-field-input": "Functions", + "no-option-found": "No option found", + "argument-source-field-input": "Source", + "argument-source-field-input-required": "Argument source is required.", + "argument-key-field-input": "Key", + "argument-key-field-input-required": "Argument key is required.", + "constant-value-field-input": "Constant value", + "constant-value-field-input-required": "Constant value is required.", + "attribute-scope-field-input": "Attribute scope", + "attribute-scope-field-input-required": "Attribute scope os required.", + "default-value-field-input": "Default value", + "type-field-input": "Type", + "type-field-input-required": "Type is required.", + "key-field-input": "Key", + "add-entity-type": "Add entity type", + "add-device-profile": "Add device profile", + "key-field-input-required": "Key is required.", + "number-floating-point-field-input": "Number of digits after floating point", + "number-floating-point-field-input-hint": "Use 0 to convert result to integer", + "add-to-message-field-input": "Add to message", + "add-to-metadata-field-input": "Add to metadata", + "custom-expression-field-input": "Mathematical Expression", + "custom-expression-field-input-required": "Mathematical expression is required", + "custom-expression-field-input-hint": "Specify a mathematical expression to evaluate. Default expression demonstrates how to transform Fahrenheit to Celsius", + "retained-message": "Retained", + "attributes-mapping": "Attributes mapping", + "latest-telemetry-mapping": "Latest telemetry mapping", + "add-mapped-attribute-to": "Add mapped attributes to", + "add-mapped-latest-telemetry-to": "Add mapped latest telemetry to", + "add-mapped-fields-to": "Add mapped fields to", + "add-selected-details-to": "Add selected details to", + "clear-selected-types": "Clear selected types", + "clear-selected-details": "Clear selected details", + "clear-selected-fields": "Clear selected fields", + "clear-selected-keys": "Clear selected keys", + "geofence-configuration": "Geofence configuration", + "coordinate-field-names": "Coordinate field names", + "coordinate-field-hint": "Rule node tries to retrieve the specified fields from the message. If they are not present, it will look them up in the metadata.", + "presence-monitoring-strategy": "Presence monitoring strategy", + "presence-monitoring-strategy-on-first-message": "On first message", + "presence-monitoring-strategy-on-each-message": "On each message", + "presence-monitoring-strategy-on-first-message-hint": "Reports presence status 'Inside' or 'Outside' on the first message after the configured minimal duration has passed since previous presence status 'Entered' or 'Left' update.", + "presence-monitoring-strategy-on-each-message-hint": "Reports presence status 'Inside' or 'Outside' on each message after presence status 'Entered' or 'Left' update.", + "fetch-credentials-to": "Fetch credentials to", + "add-originator-attributes-to": "Add originator attributes to", + "originator-attributes": "Originator attributes", + "fetch-latest-telemetry-with-timestamp": "Fetch latest telemetry with timestamp", + "fetch-latest-telemetry-with-timestamp-tooltip": "If selected, latest telemetry values will be added to the outbound metadata with timestamp, e.g: \"{{latestTsKeyName}}\": \"{\"ts\":1574329385897, \"value\":42}\"", + "tell-failure": "Tell failure if any of the attributes are missing", + "tell-failure-tooltip": "If at least one selected key doesn't exist the outbound message will report \"Failure\".", + "created-time": "Created time", + "chip-help": "Press 'Enter' to complete {{inputName}} input. \nPress 'Backspace' to delete {{inputName}}. \nMultiple values supported.", + "detail": "detail", + "field-name": "field name", + "device-profile": "device profile", + "entity-type": "entity type", + "message-type": "message type", + "timeseries-key": "time series key", + "type": "Type", + "first-name": "First name", + "last-name": "Last name", + "label": "Label", + "originator-fields-mapping": "Originator fields mapping", + "add-mapped-originator-fields-to": "Add mapped originator fields to", + "fields": "Fields", + "skip-empty-fields": "Skip empty fields", + "skip-empty-fields-tooltip": "Fields with empty values will not be added to the output message/output metadata.", + "fetch-interval": "Fetch interval", + "fetch-strategy": "Fetch strategy", + "fetch-timeseries-from-to": "Fetch time series from {{startInterval}} {{startIntervalTimeUnit}} ago to {{endInterval}} {{endIntervalTimeUnit}} ago.", + "fetch-timeseries-from-to-invalid": "Fetch time series invalid (\"Interval start\" should be less than \"Interval end\").", + "use-metadata-dynamic-interval-tooltip": "If selected, the rule node will use dynamic interval start and end based on the message and metadata patterns.", + "all-mode-hint": "If selected fetch mode \"All\" rule node will retrieve telemetry from the fetch interval with configurable query parameters.", + "first-mode-hint": "If selected fetch mode \"First\" rule node will retrieve the closest telemetry to the fetch interval's start.", + "last-mode-hint": "If selected fetch mode \"Last\" rule node will retrieve the closest telemetry to the fetch interval's end.", + "ascending": "Ascending", + "descending": "Descending", + "min": "Min", + "max": "Max", + "average": "Average", + "sum": "Sum", + "count": "Count", + "none": "None", + "last-level-relation-tooltip": "If selected, the rule node will search related entities only on the level set in the max relation level.", + "last-level-device-relation-tooltip": "If selected, the rule node will search related devices only on the level set in the max relation level.", + "data-to-fetch": "Data to fetch", + "mapping-of-customers": "Mapping of customer's", + "map-fields-required": "All mapping fields are required.", + "attributes": "Attributes", + "related-device-attributes": "Related device attributes", + "add-selected-attributes-to": "Add selected attributes to", + "device-profiles": "Device profiles", + "mapping-of-tenant": "Mapping of tenant's", + "add-attribute-key": "Add attribute key", + "message-template": "Message template", + "message-template-required": "Message template is required", + "use-system-slack-settings": "Use system slack settings", + "slack-api-token": "Slack API token", + "slack-api-token-required": "Slack API token is required", + "keys-mapping": "keys mapping", + "add-key": "Add key", + "recipients": "Recipients", + "message-subject-and-content": "Message subject and content", + "template-rules-hint": "Both input fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the message metadata.", + "originator-customer-desc": "Use customer of incoming message originator as new originator.", + "originator-tenant-desc": "Use current tenant as new originator.", + "originator-related-entity-desc": "Use related entity as new originator. Lookup based on configured relation type and direction.", + "originator-alarm-originator-desc": "Use alarm originator as new originator. Only if incoming message originator is alarm entity.", + "originator-entity-by-name-pattern-desc": "Use entity fetched from DB as new originator. Lookup based on entity type and specified name pattern.", + "email-from-template-hint": "Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", + "recipients-block-main-hint": "Comma-separated address list. All input fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", + "forward-msg-default-rule-chain": "Forward message to the originator's default rule chain", + "forward-msg-default-rule-chain-tooltip": "If enabled, message will be forwarded to the originator's default rule chain, or rule chain from configuration, if originator has no default rule chain defined in the entity profile.", + "exclude-zero-deltas": "Exclude zero deltas from outbound message", + "exclude-zero-deltas-hint": "If enabled, the \"{{outputValueKey}}\" output key will be added to the outbound message if its value is not zero.", + "exclude-zero-deltas-time-difference-hint": "If enabled, the \"{{outputValueKey}}\" and \"{{periodValueKey}}\" output keys will be added to the outbound message only if the \"{{outputValueKey}}\" value is not zero.", + "search-direction-from": "From originator to target entity", + "search-direction-to": "From target entity to originator", + "del-relation-direction-from": "From originator", + "del-relation-direction-to": "To originator", + "target-entity": "Target entity", + "function-configuration": "Function configuration", + "function-name": "Function name", + "function-name-required": "Function name is required.", + "qualifier": "Qualifier", + "qualifier-hint": "If the qualifier is not specified, the default qualifier \"$LATEST\" will be used.", + "aws-credentials": "AWS Credentials", + "connection-timeout": "Connection timeout", + "connection-timeout-required": "Connection timeout is required.", + "connection-timeout-min": "Min connection timeout is 0.", + "connection-timeout-hint": "The amount of time to wait in seconds when initially establishing a connection before giving up and timing out. A value of 0 means infinity, and is not recommended.", + "request-timeout": "Request timeout", + "request-timeout-required": "Request timeout is required", + "request-timeout-min": "Min request timeout is 0", + "request-timeout-hint": "The amount of time to wait in seconds for the request to complete before giving up and timing out. A value of 0 means infinity, and is not recommended.", + "tell-failure-aws-lambda": "Tell Failure if AWS Lambda function execution raises exception", + "tell-failure-aws-lambda-hint": "Rule node forces failure of message processing if AWS Lambda function execution raises exception.", + "key-val": { + "key": "Key", + "value": "Value", + "see-examples": "See examples.", + "remove-entry": "Remove entry", + "remove-mapping-entry": "Remove mapping entry", + "add-mapping-entry": "Add mapping", + "add-entry": "Add entry", + "copy-key-values-from": "Copy key-values from", + "delete-key-values": "Delete key-values", + "delete-key-values-from": "Delete key-values from", + "at-least-one-key-error": "At least one key should be selected.", + "unique-key-value-pair-error": "'{{keyText}}' must be different from the '{{valText}}'!" + }, + "mail-body-types": { + "plain-text": "Plain text", + "html": "HTML", + "dynamic": "Dynamic", + "use-body-type-template": "Use body type template", + "plain-text-description": "Simple, unformatted text with no special styling or formating.", + "html-text-description": "Allows you to use HTML tags for formatting, links and images in your mai body.", + "dynamic-text-description": "Allows to use Plain Text or HTML body type dynamically based on templatization feature.", + "after-template-evaluation-hint": "After template evaluation value should be true for HTML, and false for Plain text." + } + }, "timezone": { "timezone": "Time zone", "select-timezone": "Select time zone", @@ -4481,734 +5216,6 @@ "too-many-requests": "Too many requests", "too-many-updates": "Too many updates" }, - "tb": { - "rulenode": { - "id": "Id", - "additional-info": "Additional Info", - "advanced-settings": "Advanced settings", - "create-entity-if-not-exists": "Create new entity if it doesn't exist", - "create-entity-if-not-exists-hint": "If enabled, a new entity with specified parameters will be created unless it already exists. Existing entities will be used as is for relation.", - "select-device-connectivity-event": "Select device connectivity event", - "entity-name-pattern": "Name pattern", - "device-name-pattern": "Device name", - "asset-name-pattern": "Asset name", - "entity-view-name-pattern": "Entity view name", - "customer-title-pattern": "Customer title", - "dashboard-name-pattern": "Dashboard title", - "user-name-pattern": "User email", - "edge-name-pattern": "Edge name", - "entity-name-pattern-required": "Name pattern is required", - "entity-name-pattern-hint": "Name pattern field support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", - "copy-message-type": "Copy message type", - "entity-type-pattern": "Type pattern", - "entity-type-pattern-required": "Type pattern is required", - "message-type-value": "Message type value", - "message-type-value-required": "Message type value is required", - "message-type-value-max-length": "Message type value should be less than 256", - "output-message-type": "Output message type", - "entity-cache-expiration": "Entities cache expiration time (sec)", - "entity-cache-expiration-hint": "Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.", - "entity-cache-expiration-required": "Entities cache expiration time is required.", - "entity-cache-expiration-range": "Entities cache expiration time should be greater than or equal to 0.", - "customer-name-pattern": "Customer title", - "customer-name-pattern-required": "Customer title is required", - "customer-name-pattern-hint": "Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", - "create-customer-if-not-exists": "Create new customer if it doesn't exist", - "unassign-from-customer": "Unassign from specific customer if originator is dashboard", - "unassign-from-customer-tooltip": "Only dashboards can be assigned to multiple customers at once. \nIf the message originator is a dashboard, you need to explicitly specify the customer's title to unassign from.", - "customer-cache-expiration": "Customers cache expiration time (sec)", - "customer-cache-expiration-hint": "Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.", - "customer-cache-expiration-required": "Customers cache expiration time is required.", - "customer-cache-expiration-range": "Customers cache expiration time should be greater than or equal to 0.", - "interval-start": "Interval start", - "interval-end": "Interval end", - "time-unit": "Time unit", - "fetch-mode": "Fetch mode", - "order-by-timestamp": "Order by timestamp", - "limit": "Limit", - "limit-hint": "Min limit value is 2, max - 1000. If you want to fetch a single entry, select fetch mode 'First' or 'Last'.", - "limit-required": "Limit is required.", - "limit-range": "Limit should be in a range from 2 to 1000.", - "time-unit-milliseconds": "Milliseconds", - "time-unit-seconds": "Seconds", - "time-unit-minutes": "Minutes", - "time-unit-hours": "Hours", - "time-unit-days": "Days", - "time-value-range": "Allowing range from 1 to 2147483647.", - "start-interval-value-required": "Interval start is required.", - "end-interval-value-required": "Interval end is required.", - "filter": "Filter", - "switch": "Switch", - "math-templatization-tooltip": "This field support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", - "add-message-type": "Add message type", - "select-message-types-required": "At least one message type should be selected.", - "select-message-types": "Select message types", - "no-message-types-found": "No message types found", - "no-message-type-matching": "'{{messageType}}' not found.", - "create-new-message-type": "Create a new one.", - "message-types-required": "Message types are required.", - "client-attributes": "Client attributes", - "shared-attributes": "Shared attributes", - "server-attributes": "Server attributes", - "attributes-keys": "Attributes keys", - "attributes-keys-required": "Attributes keys are required", - "attributes-scope": "Attributes scope", - "attributes-scope-value": "Attributes scope value", - "attributes-scope-value-copy": "Copy attributes scope value", - "attributes-scope-hint": "Use the 'scope' metadata key to dynamically set the attribute scope per message. If provided, this overrides the scope set in the configuration.", - "notify-device": "Force notification to the device", - "send-attributes-updated-notification": "Send attributes updated notification", - "send-attributes-updated-notification-hint": "Send notification about updated attributes as a separate message to the rule engine queue.", - "send-attributes-deleted-notification": "Send attributes deleted notification", - "send-attributes-deleted-notification-hint": "Send notification about deleted attributes as a separate message to the rule engine queue.", - "update-attributes-only-on-value-change": "Save attributes only if the value changes", - "update-attributes-only-on-value-change-hint": "Updates the attributes on every incoming message disregarding if their value has changed. Increases API usage and reduces performance.", - "update-attributes-only-on-value-change-hint-enabled": "Updates the attributes only if their value has changed. If the value is not changed, no update to the attribute timestamp nor attribute change notification will be sent.", - "fetch-credentials-to-metadata": "Fetch credentials to metadata", - "notify-device-on-update-hint": "If enabled, force notification to the device about shared attributes update. If disabled, the notification behavior is controlled by the 'notifyDevice' parameter from the incoming message metadata. To turn off the notification, the message metadata must contain the 'notifyDevice' parameter set to 'false'. Any other case will trigger the notification to the device.", - "notify-device-on-delete-hint": "If enabled, force notification to the device about shared attributes removal. If disabled, the notification behavior is controlled by the 'notifyDevice' parameter from the incoming message metadata. To turn on the notification, the message metadata must contain the 'notifyDevice' parameter set to 'true'. In any other case, the notification will not be triggered to the device.", - "latest-timeseries": "Latest time series data keys", - "timeseries-keys": "Time series keys", - "timeseries-keys-required": "At least one time series key should be selected.", - "add-timeseries-key": "Add time series key", - "add-message-field": "Add message field", - "relation-search-parameters": "Relation search parameters", - "relation-parameters": "Relation parameters", - "add-metadata-field": "Add metadata field", - "data-keys": "Message field names", - "copy-from": "Copy from", - "data-to-metadata": "Data to metadata", - "metadata-to-data": "Metadata to data", - "use-regular-expression-hint": "Use regular expression to copy keys by pattern.\n\nTips & tricks:\nPress 'Enter' to complete field name input.\nPress 'Backspace' to delete field name. Multiple field names supported.", - "interval": "Interval", - "interval-required": "Interval is required", - "interval-hint": "Deduplication interval in seconds.", - "interval-min-error": "Min allowed value is 1", - "max-pending-msgs": "Max pending messages", - "max-pending-msgs-hint": "Maximum number of messages that are stored in memory for each unique deduplication id.", - "max-pending-msgs-required": "Max pending messages is required", - "max-pending-msgs-max-error": "Max allowed value is 1000", - "max-pending-msgs-min-error": "Min allowed value is 1", - "max-retries": "Max retries", - "max-retries-required": "Max retries is required", - "max-retries-hint": "Maximum number of retries to push the deduplicated messages into the queue. 10 seconds delay is used between retries", - "max-retries-max-error": "Max allowed value is 100", - "max-retries-min-error": "Min allowed value is 0", - "strategy": "Strategy", - "strategy-required": "Strategy is required", - "strategy-all-hint": "Return all messages that arrived during deduplication period as a single JSON array message. Where each element represents object with msg and metadata inner properties.", - "strategy-first-hint": "Return first message that arrived during deduplication period.", - "strategy-last-hint": "Return last message that arrived during deduplication period.", - "first": "First", - "last": "Last", - "all": "All", - "output-msg-type-hint": "The message type of the deduplication result.", - "queue-name-hint": "The queue name where the deduplication result will be published.", - "keys": "Keys", - "keys-required": "Keys are required", - "rename-keys-in": "Rename keys in", - "data": "Data", - "message": "Message", - "metadata": "Metadata", - "current-key-name": "Current key name", - "key-name-required": "Key name is required", - "new-key-name": "New key name", - "new-key-name-required": "New key name is required", - "metadata-keys": "Metadata field names", - "json-path-expression": "JSON path expression", - "json-path-expression-required": "JSON path expression is required", - "json-path-expression-hint": "JSONPath specifies a path to an element or a set of elements in a JSON structure. '$' represents the root object or array.", - "relations-query": "Relations query", - "device-relations-query": "Device relations query", - "max-relation-level": "Max relation level", - "max-relation-level-error": "Value should be greater than 0 or unspecified.", - "max-relation-level-invalid": "Value should be an integer.", - "relation-type": "Relation type", - "relation-type-pattern": "Relation type pattern", - "relation-type-pattern-required": "Relation type pattern is required", - "relation-types-list": "Relation types to propagate", - "relation-types-list-hint": "If Propagate relation types are not selected, alarms will be propagated without filtering by relation type.", - "unlimited-level": "Unlimited level", - "latest-telemetry": "Latest telemetry", - "add-telemetry-key": "Add telemetry key", - "delete-from": "Delete from", - "use-regular-expression-delete-hint": "Use regular expression to delete keys by pattern.\n\nTips & tricks:\nPress 'Enter' to complete field name input.\nPress 'Backspace' to delete field name.\nMultiple field names supported.", - "fetch-into": "Fetch into", - "attr-mapping": "Attributes mapping:", - "source-attribute": "Source attribute key", - "source-attribute-required": "Source attribute key is required.", - "source-telemetry": "Source telemetry key", - "source-telemetry-required": "Source telemetry key is required.", - "target-key": "Target key", - "target-key-required": "Target key is required.", - "attr-mapping-required": "At least one mapping entry should be specified.", - "fields-mapping": "Fields mapping", - "fields-mapping-hint": "If the message field is set to $entityId, the message originator's id will be saved to the specified table column.", - "relations-query-config-direction-suffix": "originator", - "profile-name": "Profile name", - "fetch-circle-parameter-info-from-metadata-hint": "Metadata field '{{perimeterKeyName}}' should be defined in next format: {\"latitude\":48.196, \"longitude\":24.6532, \"radius\":100.0, \"radiusUnit\":\"METER\"}", - "fetch-poligon-parameter-info-from-metadata-hint": "Metadata field '{{perimeterKeyName}}' should be defined in next format: [[48.19736,24.65235],[48.19800,24.65060],...,[48.19849,24.65420]]", - "short-templatization-tooltip": "Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", - "fields-mapping-required": "At least one field mapping should be specified.", - "at-least-one-field-required": "At least one input field must have a value(s) provided.", - "originator-fields-sv-map-hint": "Target key fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", - "sv-map-hint": "Only target key fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", - "source-field": "Source field", - "source-field-required": "Source field is required.", - "originator-source": "Originator source", - "new-originator": "New originator", - "originator-customer": "Customer", - "originator-tenant": "Tenant", - "originator-related": "Related entity", - "originator-alarm-originator": "Alarm Originator", - "originator-entity": "Entity by name pattern", - "clone-message": "Clone message", - "transform": "Transform", - "default-ttl": "Default TTL in seconds", - "default-ttl-required": "Default TTL is required.", - "default-ttl-hint": "Rule node will fetch Time-to-Live (TTL) value from the message metadata. If no value is present, it defaults to the TTL specified in the configuration. If the value is set to 0, the TTL from the tenant profile configuration will be applied.", - "default-ttl-zero-hint": "TTL will not be applied if its value is set to 0.", - "min-default-ttl-message": "Only 0 minimum TTL is allowed.", - "generation-parameters": "Generation parameters", - "message-count": "Generated messages limit (0 - unlimited)", - "message-count-required": "Generated messages limit is required.", - "min-message-count-message": "Only 0 minimum message count is allowed.", - "period-seconds": "Period in seconds", - "period-seconds-required": "Period is required.", - "generation-frequency-seconds": "Generation frequency in seconds", - "generation-frequency-required": "Generation frequency is required.", - "min-generation-frequency-message": "Only 1 second minimum is allowed.", - "script-lang-tbel": "TBEL", - "script-lang-js": "JS", - "use-metadata-period-in-seconds-patterns": "Use period in seconds pattern", - "use-metadata-period-in-seconds-patterns-hint": "If selected, rule node use period in seconds interval pattern from message metadata or data assuming that intervals are in the seconds.", - "period-in-seconds-pattern": "Period in seconds pattern", - "period-in-seconds-pattern-required": "Period in seconds pattern is required", - "min-period-seconds-message": "Only 1 second minimum period is allowed.", - "originator": "Originator", - "message-body": "Message body", - "message-metadata": "Message metadata", - "generate": "Generate", - "current-rule-node": "Current Rule Node", - "current-tenant": "Current Tenant", - "generator-function": "Generator function", - "test-generator-function": "Test generator function", - "generator": "Generator", - "test-filter-function": "Test filter function", - "test-switch-function": "Test switch function", - "test-transformer-function": "Test transformer function", - "transformer": "Transformer", - "alarm-create-condition": "Alarm create condition", - "test-condition-function": "Test condition function", - "alarm-clear-condition": "Alarm clear condition", - "alarm-details-builder": "Alarm details builder", - "test-details-function": "Test details function", - "alarm-type": "Alarm type", - "select-entity-types": "Select entity types", - "alarm-type-required": "Alarm type is required.", - "alarm-severity": "Alarm severity", - "alarm-severity-required": "Alarm severity is required", - "alarm-severity-pattern": "Alarm severity pattern", - "alarm-status-filter": "Alarm status filter", - "alarm-status-list-empty": "Alarm status list is empty", - "no-alarm-status-matching": "No alarm status matching were found.", - "propagate": "Propagate alarm to related entities", - "propagate-to-owner": "Propagate alarm to entity owner (Customer or Tenant)", - "propagate-to-tenant": "Propagate alarm to Tenant", - "condition": "Condition", - "details": "Details", - "to-string": "To string", - "test-to-string-function": "Test to string function", - "from-template": "From", - "from-template-required": "From is required", - "message-to-metadata": "Message to metadata", - "metadata-to-message": "Metadata to message", - "from-message": "From message", - "from-metadata": "From metadata", - "to-template": "To", - "to-template-required": "To Template is required", - "mail-address-list-template-hint": "Comma separated address list, use ${metadataKey} for value from metadata, $[messageKey] for value from message body", - "cc-template": "Cc", - "bcc-template": "Bcc", - "subject-template": "Subject", - "subject-template-required": "Subject Template is required", - "body-template": "Body", - "body-template-required": "Body Template is required", - "dynamic-mail-body-type": "Dynamic mail body type", - "mail-body-type": "Mail body type", - "body-type-template": "Body type template", - "reply-routing-configuration": "Reply Routing Configuration", - "rpc-reply-routing-configuration-hint": "These configuration parameters specify the metadata key names used to identify the service, session, and request for sending a reply back.", - "reply-routing-configuration-hint": "These configuration parameters specify the metadata key names used to identify the service and request for sending a reply back.", - "request-id-metadata-attribute": "Request Id", - "service-id-metadata-attribute": "Service Id", - "session-id-metadata-attribute": "Session Id", - "timeout-sec": "Timeout in seconds", - "timeout-required": "Timeout is required", - "min-timeout-message": "Only 0 minimum timeout value is allowed.", - "endpoint-url-pattern": "Endpoint URL pattern", - "endpoint-url-pattern-required": "Endpoint URL pattern is required", - "request-method": "Request method", - "use-simple-client-http-factory": "Use simple client HTTP factory", - "ignore-request-body": "Without request body", - "parse-to-plain-text": "Parse to plain text", - "parse-to-plain-text-hint": "If selected, request body message payload will be transformed from JSON string to plain text, e.g. msg = \"Hello,\\t\"world\"\" will be parsed to Hello, \"world\"", - "read-timeout": "Read timeout in millis", - "read-timeout-hint": "The value of 0 means an infinite timeout", - "max-parallel-requests-count": "Max number of parallel requests", - "max-parallel-requests-count-hint": "The value of 0 specifies no limit in parallel processing", - "max-response-size": "Max response size (in KB)", - "max-response-size-hint": "The maximum amount of memory allocated for buffering data when decoding or encoding HTTP messages, such as JSON or XML payloads", - "headers": "Headers", - "headers-hint": "Use ${metadataKey} for value from metadata, $[messageKey] for value from message body in header/value fields", - "header": "Header", - "header-required": "Header is required", - "value": "Value", - "value-required": "Value is required", - "topic-pattern": "Topic pattern", - "key-pattern": "Key pattern", - "key-pattern-hint": "Optional. If a valid partition number is specified, it will be used when sending the record. If no partition is specified, the key will be used instead. If neither is specified, a partition will be assigned in a round-robin fashion.", - "topic-pattern-required": "Topic pattern is required", - "topic": "Topic", - "topic-required": "Topic is required", - "bootstrap-servers": "Bootstrap servers", - "bootstrap-servers-required": "Bootstrap servers value is required", - "other-properties": "Other properties", - "key": "Key", - "key-required": "Key is required", - "retries": "Automatically retry times if fails", - "min-retries-message": "Only 0 minimum retries is allowed.", - "batch-size-bytes": "Produces batch size in bytes", - "min-batch-size-bytes-message": "Only 0 minimum batch size is allowed.", - "linger-ms": "Time to buffer locally (ms)", - "min-linger-ms-message": "Only 0 ms minimum value is allowed.", - "buffer-memory-bytes": "Client buffer max size in bytes", - "min-buffer-memory-message": "Only 0 minimum buffer size is allowed.", - "memory-buffer-size-range": "Memory buffer size must be between 0 and {{max}} KB", - "acks": "Number of acknowledgments", - "key-serializer": "Key serializer", - "key-serializer-required": "Key serializer is required", - "value-serializer": "Value serializer", - "value-serializer-required": "Value serializer is required", - "topic-arn-pattern": "Topic ARN pattern", - "topic-arn-pattern-required": "Topic ARN pattern is required", - "aws-access-key-id": "AWS Access Key ID", - "aws-access-key-id-required": "AWS Access Key ID is required", - "aws-secret-access-key": "AWS Secret Access Key", - "aws-secret-access-key-required": "AWS Secret Access Key is required", - "aws-region": "AWS Region", - "aws-region-required": "AWS Region is required", - "exchange-name-pattern": "Exchange name pattern", - "routing-key-pattern": "Routing key pattern", - "message-properties": "Message properties", - "host": "Host", - "host-required": "Host is required", - "port": "Port", - "port-required": "Port is required", - "port-range": "Port should be in a range from 1 to 65535.", - "virtual-host": "Virtual host", - "username": "Username", - "password": "Password", - "automatic-recovery": "Automatic recovery", - "connection-timeout-ms": "Connection timeout (ms)", - "min-connection-timeout-ms-message": "Only 0 ms minimum value is allowed.", - "handshake-timeout-ms": "Handshake timeout (ms)", - "min-handshake-timeout-ms-message": "Only 0 ms minimum value is allowed.", - "client-properties": "Client properties", - "queue-url-pattern": "Queue URL pattern", - "queue-url-pattern-required": "Queue URL pattern is required", - "delay-seconds": "Delay (seconds)", - "min-delay-seconds-message": "Only 0 seconds minimum value is allowed.", - "max-delay-seconds-message": "Only 900 seconds maximum value is allowed.", - "name": "Name", - "name-required": "Name is required", - "queue-type": "Queue type", - "sqs-queue-standard": "Standard", - "sqs-queue-fifo": "FIFO", - "gcp-project-id": "GCP project ID", - "gcp-project-id-required": "GCP project ID is required", - "gcp-service-account-key": "GCP service account key file", - "gcp-service-account-key-required": "GCP service account key file is required", - "pubsub-topic-name": "Topic name", - "pubsub-topic-name-required": "Topic name is required", - "message-attributes": "Message attributes", - "message-attributes-hint": "Use ${metadataKey} for value from metadata, $[messageKey] for value from message body in name/value fields", - "connect-timeout": "Connection timeout (sec)", - "connect-timeout-required": "Connection timeout is required.", - "connect-timeout-range": "Connection timeout should be in a range from 1 to 200.", - "client-id": "Client ID", - "client-id-hint": "Optional. Leave empty for auto-generated Client ID. Be careful when specifying the Client ID. Majority of the MQTT brokers will not allow multiple connections with the same Client ID. To connect to such brokers, your mqtt Client ID must be unique. When platform is running in a micro-services mode, the copy of rule node is launched in each micro-service. This will automatically lead to multiple mqtt clients with the same ID and may cause failures of the rule node. To avoid such failures enable \"Add Service ID as suffix to Client ID\" option below.", - "append-client-id-suffix": "Add Service ID as suffix to Client ID", - "client-id-suffix-hint": "Optional. Applied when \"Client ID\" specified explicitly. If selected then Service ID will be added to Client ID as a suffix. Helps to avoid failures when platform is running in a micro-services mode.", - "device-id": "Device ID", - "device-id-required": "Device ID is required.", - "clean-session": "Clean session", - "enable-ssl": "Enable SSL", - "credentials": "Credentials", - "credentials-type": "Credentials type", - "credentials-type-required": "Credentials type is required.", - "credentials-anonymous": "Anonymous", - "credentials-basic": "Basic", - "credentials-pem": "PEM", - "credentials-pem-hint": "At least Server CA certificate file or a pair of Client certificate and Client private key files are required", - "credentials-sas": "Shared Access Signature", - "sas-key": "SAS Key", - "sas-key-required": "SAS Key is required.", - "hostname": "Hostname", - "hostname-required": "Hostname is required.", - "azure-ca-cert": "CA certificate file", - "username-required": "Username is required.", - "password-required": "Password is required.", - "ca-cert": "Server CA certificate file", - "private-key": "Client private key file", - "cert": "Client certificate file", - "no-file": "No file selected.", - "drop-file": "Drop a file or click to select a file to upload.", - "private-key-password": "Private key password", - "use-system-smtp-settings": "Use system SMTP settings", - "use-metadata-dynamic-interval": "Use dynamic interval", - "metadata-dynamic-interval-hint": "Interval start and end input fields support templatization. Note that the substituted template value should be set in milliseconds. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", - "use-metadata-interval-patterns-hint": "If selected, rule node use start and end interval patterns from message metadata or data assuming that intervals are in the milliseconds.", - "use-message-alarm-data": "Use message alarm data", - "overwrite-alarm-details": "Overwrite alarm details", - "use-alarm-severity-pattern": "Use alarm severity pattern", - "check-all-keys": "Check that all specified fields are present", - "check-all-keys-hint": "If selected, checks that all specified keys are present in the message data and metadata.", - "check-relation-to-specific-entity": "Check relation to specific entity", - "check-relation-to-specific-entity-tooltip": "If enabled, checks the presence of relation with a specific entity otherwise, checks the presence of relation with any entity. In both cases, relation lookup is based on configured direction and type.", - "check-relation-hint": "Checks existence of relation to specific entity or to any entity based on direction and relation type.", - "delete-relation-with-specific-entity": "Delete relation with specific entity", - "delete-relation-with-specific-entity-hint": "If enabled, will delete the relation with just one specific entity. Otherwise, the relation will be removed with all matching entities.", - "delete-relation-hint": "Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.", - "remove-current-relations": "Remove current relations", - "remove-current-relations-hint": "Removes current relations from the originator of the incoming message based on direction and type.", - "change-originator-to-related-entity": "Change originator to related entity", - "change-originator-to-related-entity-hint": "Used to process submitted message as a message from another entity.", - "start-interval": "Interval start", - "end-interval": "Interval end", - "start-interval-required": "Interval start is required.", - "end-interval-required": "Interval end is required.", - "smtp-protocol": "Protocol", - "smtp-host": "SMTP host", - "smtp-host-required": "SMTP host is required.", - "smtp-port": "SMTP port", - "smtp-port-required": "You must supply a smtp port.", - "smtp-port-range": "SMTP port should be in a range from 1 to 65535.", - "timeout-msec": "Timeout ms", - "min-timeout-msec-message": "Only 0 ms minimum value is allowed.", - "enter-username": "Enter username", - "enter-password": "Enter password", - "enable-tls": "Enable TLS", - "tls-version": "TLS version", - "enable-proxy": "Enable proxy", - "use-system-proxy-properties": "Use system proxy properties", - "proxy-host": "Proxy host", - "proxy-host-required": "Proxy host is required.", - "proxy-port": "Proxy port", - "proxy-port-required": "Proxy port is required.", - "proxy-port-range": "Proxy port should be in a range from 1 to 65535.", - "proxy-user": "Proxy user", - "proxy-password": "Proxy password", - "proxy-scheme": "Proxy scheme", - "numbers-to-template": "Phone Numbers To Template", - "numbers-to-template-required": "Phone Numbers To Template is required", - "numbers-to-template-hint": "Comma separated Phone Numbers, use ${metadataKey} for value from metadata, $[messageKey] for value from message body", - "sms-message-template": "SMS message Template", - "sms-message-template-required": "SMS message Template is required", - "use-system-sms-settings": "Use system SMS provider settings", - "min-period-0-seconds-message": "Only 0 second minimum period is allowed.", - "max-pending-messages": "Maximum pending messages", - "max-pending-messages-required": "Maximum pending messages is required.", - "max-pending-messages-range": "Maximum pending messages should be in a range from 1 to 100000.", - "originator-types-filter": "Originator types filter", - "interval-seconds": "Interval in seconds", - "interval-seconds-required": "Interval is required.", - "int-range": "Value must not exceed the maximum integer limit (2147483648)", - "min-interval-seconds-message": "Only 1 second minimum interval is allowed.", - "output-timeseries-key-prefix": "Output time series key prefix", - "output-timeseries-key-prefix-required": "Output time series key prefix required.", - "separator-hint": "You should press \"Enter\" to complete field input.", - "select-details": "Select details", - "entity-details-id": "Id", - "entity-details-title": "Title", - "entity-details-country": "Country", - "entity-details-state": "State", - "entity-details-city": "City", - "entity-details-zip": "Zip", - "entity-details-address": "Address", - "entity-details-address2": "Address2", - "entity-details-additional_info": "Additional Info", - "entity-details-phone": "Phone", - "entity-details-email": "Email", - "email-sender": "Email sender", - "fields-to-check": "Fields to check", - "add-detail": "Add detail", - "check-all-keys-tooltip": "If enabled, checks the presence of all fields listed in the message and metadata field names within the incoming message and its metadata.", - "fields-to-check-hint": "Press \"Enter\" to complete field name input. Multiple field names supported.", - "entity-details-list-empty": "At least one detail should be selected.", - "alarm-status": "Alarm status", - "alarm-required": "At least one alarm status should be selected.", - "no-entity-details-matching": "No entity details matching were found.", - "custom-table-name": "Custom table name", - "custom-table-name-required": "Table Name is required", - "custom-table-hint": "The table must be created in your Cassandra cluster and its name must start with the prefix 'cs_tb_' to avoid the data insertion to the common TB tables. Enter the table name here without the 'cs_tb_' prefix.", - "message-field": "Message field", - "message-field-required": "Message field is required.", - "table-col": "Table column", - "table-col-required": "Table column is required.", - "latitude-field-name": "Latitude field name", - "longitude-field-name": "Longitude field name", - "latitude-field-name-required": "Latitude field name is required.", - "longitude-field-name-required": "Longitude field name is required.", - "fetch-perimeter-info-from-metadata": "Fetch perimeter information from metadata", - "fetch-perimeter-info-from-metadata-tooltip": "If perimeter type is set to 'Polygon' the value of metadata field '{{perimeterKeyName}}' will be set as perimeter definition without additional parsing of the value. Otherwise, if perimeter type is set to 'Circle' the value of '{{perimeterKeyName}}' metadata field will be parsed to extract 'latitude', 'longitude', 'radius', 'radiusUnit' fields that uses for circle perimeter definition.", - "perimeter-key-name": "Perimeter key name", - "perimeter-key-name-hint": "Metadata field name that includes perimeter information.", - "perimeter-key-name-required": "Perimeter key name is required.", - "perimeter-circle": "Circle", - "perimeter-polygon": "Polygon", - "perimeter-type": "Perimeter type", - "circle-center-latitude": "Center latitude", - "circle-center-latitude-required": "Center latitude is required.", - "circle-center-longitude": "Center longitude", - "circle-center-longitude-required": "Center longitude is required.", - "range-unit-meter": "Meter", - "range-unit-kilometer": "Kilometer", - "range-unit-foot": "Foot", - "range-unit-mile": "Mile", - "range-unit-nautical-mile": "Nautical mile", - "range-units": "Range units", - "range-units-required": "Range units is required.", - "range": "Range", - "range-required": "Range is required.", - "polygon-definition": "Polygon definition", - "polygon-definition-required": "Polygon definition is required.", - "polygon-definition-hint": "Use the following format for manual definition of polygon: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].", - "min-inside-duration": "Minimal inside duration", - "min-inside-duration-value-required": "Minimal inside duration is required", - "min-inside-duration-time-unit": "Minimal inside duration time unit", - "min-outside-duration": "Minimal outside duration", - "min-outside-duration-value-required": "Minimal outside duration is required", - "min-outside-duration-time-unit": "Minimal outside duration time unit", - "tell-failure-if-absent": "Tell Failure", - "tell-failure-if-absent-hint": "If at least one selected key doesn't exist the outbound message will report \"Failure\".", - "get-latest-value-with-ts": "Fetch timestamp for the latest telemetry values", - "get-latest-value-with-ts-hint": "If selected, the latest telemetry values will also include timestamp, e.g: \"temp\": \"{\"ts\":1574329385897, \"value\":42}\"", - "ignore-null-strings": "Ignore null strings", - "ignore-null-strings-hint": "If selected rule node will ignore entity fields with empty value.", - "add-metadata-key-values-as-kafka-headers": "Add Message metadata key-value pairs to Kafka record headers", - "add-metadata-key-values-as-kafka-headers-hint": "If selected, key-value pairs from message metadata will be added to the outgoing records headers as byte arrays with predefined charset encoding.", - "charset-encoding": "Charset encoding", - "charset-encoding-required": "Charset encoding is required.", - "charset-us-ascii": "US-ASCII", - "charset-iso-8859-1": "ISO-8859-1", - "charset-utf-8": "UTF-8", - "charset-utf-16be": "UTF-16BE", - "charset-utf-16le": "UTF-16LE", - "charset-utf-16": "UTF-16", - "select-queue-hint": "The queue name can be selected from a drop-down list or add a custom name.", - "device-profile-node-hint": "Useful if you have duration or repeating conditions to ensure continuity of alarm state evaluation.", - "persist-alarm-rules": "Persist state of alarm rules", - "persist-alarm-rules-hint": "If enabled, the rule node will store the state of processing to the database.", - "fetch-alarm-rules": "Fetch state of alarm rules", - "fetch-alarm-rules-hint": "If enabled, the rule node will restore the state of processing on initialization and ensure that alarms are raised even after server restarts. Otherwise, the state will be restored when the first message from the device arrives.", - "input-value-key": "Input value key", - "input-value-key-required": "Input value key is required.", - "output-value-key": "Output value key", - "output-value-key-required": "Output value key is required.", - "number-of-digits-after-floating-point": "Number of digits after floating point", - "number-of-digits-after-floating-point-range": "Number of digits after floating point should be in a range from 0 to 15.", - "failure-if-delta-negative": "Tell Failure if delta is negative", - "failure-if-delta-negative-tooltip": "Rule node forces failure of message processing if delta value is negative.", - "use-caching": "Use caching", - "use-caching-tooltip": "Rule node will cache the value of \"{{inputValueKey}}\" that arrives from the incoming message to improve performance. Note that the cache will not be updated if you modify the \"{{inputValueKey}}\" value elsewhere.", - "add-time-difference-between-readings": "Add the time difference between \"{{inputValueKey}}\" readings", - "add-time-difference-between-readings-tooltip": "If enabled, the rule node will add the \"{{periodValueKey}}\" to the outbound message.", - "period-value-key": "Period value key", - "period-value-key-required": "Period value key is required.", - "general-pattern-hint": "Use ${metadataKey} for value from metadata, $[messageKey] for value from message body.", - "alarm-severity-pattern-hint": "Use ${metadataKey} for value from metadata, $[messageKey] for value from message body. Alarm severity should be system (CRITICAL, MAJOR etc.)", - "output-node-name-hint": "The 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.", - "skip-latest-persistence": "Skip latest persistence", - "skip-latest-persistence-hint": "Rule node will not update values for incoming keys for the latest time series data. Useful for highly loaded use-cases to decrease the pressure on the DB.", - "use-server-ts": "Use server ts", - "use-server-ts-hint": "Rule node will use the timestamp of message processing instead of the timestamp from the message. Useful for all sorts of sequential processing if you merge messages from multiple sources (devices, assets, etc).", - "kv-map-pattern-hint": "All input fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", - "kv-map-single-pattern-hint": "Input field support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", - "shared-scope": "Shared scope", - "server-scope": "Server scope", - "client-scope": "Client scope", - "attribute-type": "Attribute", - "constant-type": "Constant", - "time-series-type": "Time series", - "message-body-type": "Message", - "message-metadata-type": "Metadata", - "argument-tile": "Arguments", - "no-arguments-prompt": "No arguments configured", - "result-title": "Result", - "functions-field-input": "Functions", - "no-option-found": "No option found", - "argument-source-field-input": "Source", - "argument-source-field-input-required": "Argument source is required.", - "argument-key-field-input": "Key", - "argument-key-field-input-required": "Argument key is required.", - "constant-value-field-input": "Constant value", - "constant-value-field-input-required": "Constant value is required.", - "attribute-scope-field-input": "Attribute scope", - "attribute-scope-field-input-required": "Attribute scope os required.", - "default-value-field-input": "Default value", - "type-field-input": "Type", - "type-field-input-required": "Type is required.", - "key-field-input": "Key", - "add-entity-type": "Add entity type", - "add-device-profile": "Add device profile", - "key-field-input-required": "Key is required.", - "number-floating-point-field-input": "Number of digits after floating point", - "number-floating-point-field-input-hint": "Use 0 to convert result to integer", - "add-to-message-field-input": "Add to message", - "add-to-metadata-field-input": "Add to metadata", - "custom-expression-field-input": "Mathematical Expression", - "custom-expression-field-input-required": "Mathematical expression is required", - "custom-expression-field-input-hint": "Specify a mathematical expression to evaluate. Default expression demonstrates how to transform Fahrenheit to Celsius", - "retained-message": "Retained", - "attributes-mapping": "Attributes mapping", - "latest-telemetry-mapping": "Latest telemetry mapping", - "add-mapped-attribute-to": "Add mapped attributes to", - "add-mapped-latest-telemetry-to": "Add mapped latest telemetry to", - "add-mapped-fields-to": "Add mapped fields to", - "add-selected-details-to": "Add selected details to", - "clear-selected-types": "Clear selected types", - "clear-selected-details": "Clear selected details", - "clear-selected-fields": "Clear selected fields", - "clear-selected-keys": "Clear selected keys", - "geofence-configuration": "Geofence configuration", - "coordinate-field-names": "Coordinate field names", - "coordinate-field-hint": "Rule node tries to retrieve the specified fields from the message. If they are not present, it will look them up in the metadata.", - "presence-monitoring-strategy": "Presence monitoring strategy", - "presence-monitoring-strategy-on-first-message": "On first message", - "presence-monitoring-strategy-on-each-message": "On each message", - "presence-monitoring-strategy-on-first-message-hint": "Reports presence status 'Inside' or 'Outside' on the first message after the configured minimal duration has passed since previous presence status 'Entered' or 'Left' update.", - "presence-monitoring-strategy-on-each-message-hint": "Reports presence status 'Inside' or 'Outside' on each message after presence status 'Entered' or 'Left' update.", - "fetch-credentials-to": "Fetch credentials to", - "add-originator-attributes-to": "Add originator attributes to", - "originator-attributes": "Originator attributes", - "fetch-latest-telemetry-with-timestamp": "Fetch latest telemetry with timestamp", - "fetch-latest-telemetry-with-timestamp-tooltip": "If selected, latest telemetry values will be added to the outbound metadata with timestamp, e.g: \"{{latestTsKeyName}}\": \"{\"ts\":1574329385897, \"value\":42}\"", - "tell-failure": "Tell failure if any of the attributes are missing", - "tell-failure-tooltip": "If at least one selected key doesn't exist the outbound message will report \"Failure\".", - "created-time": "Created time", - "chip-help": "Press 'Enter' to complete {{inputName}} input. \nPress 'Backspace' to delete {{inputName}}. \nMultiple values supported.", - "detail": "detail", - "field-name": "field name", - "device-profile": "device profile", - "entity-type": "entity type", - "message-type": "message type", - "timeseries-key": "time series key", - "type": "Type", - "first-name": "First name", - "last-name": "Last name", - "label": "Label", - "originator-fields-mapping": "Originator fields mapping", - "add-mapped-originator-fields-to": "Add mapped originator fields to", - "fields": "Fields", - "skip-empty-fields": "Skip empty fields", - "skip-empty-fields-tooltip": "Fields with empty values will not be added to the output message/output metadata.", - "fetch-interval": "Fetch interval", - "fetch-strategy": "Fetch strategy", - "fetch-timeseries-from-to": "Fetch time series from {{startInterval}} {{startIntervalTimeUnit}} ago to {{endInterval}} {{endIntervalTimeUnit}} ago.", - "fetch-timeseries-from-to-invalid": "Fetch time series invalid (\"Interval start\" should be less than \"Interval end\").", - "use-metadata-dynamic-interval-tooltip": "If selected, the rule node will use dynamic interval start and end based on the message and metadata patterns.", - "all-mode-hint": "If selected fetch mode \"All\" rule node will retrieve telemetry from the fetch interval with configurable query parameters.", - "first-mode-hint": "If selected fetch mode \"First\" rule node will retrieve the closest telemetry to the fetch interval's start.", - "last-mode-hint": "If selected fetch mode \"Last\" rule node will retrieve the closest telemetry to the fetch interval's end.", - "ascending": "Ascending", - "descending": "Descending", - "min": "Min", - "max": "Max", - "average": "Average", - "sum": "Sum", - "count": "Count", - "none": "None", - "last-level-relation-tooltip": "If selected, the rule node will search related entities only on the level set in the max relation level.", - "last-level-device-relation-tooltip": "If selected, the rule node will search related devices only on the level set in the max relation level.", - "data-to-fetch": "Data to fetch", - "mapping-of-customers": "Mapping of customer's", - "map-fields-required": "All mapping fields are required.", - "attributes": "Attributes", - "related-device-attributes": "Related device attributes", - "add-selected-attributes-to": "Add selected attributes to", - "device-profiles": "Device profiles", - "mapping-of-tenant": "Mapping of tenant's", - "add-attribute-key": "Add attribute key", - "message-template": "Message template", - "message-template-required": "Message template is required", - "use-system-slack-settings": "Use system slack settings", - "slack-api-token": "Slack API token", - "slack-api-token-required": "Slack API token is required", - "keys-mapping": "keys mapping", - "add-key": "Add key", - "recipients": "Recipients", - "message-subject-and-content": "Message subject and content", - "template-rules-hint": "Both input fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the message metadata.", - "originator-customer-desc": "Use customer of incoming message originator as new originator.", - "originator-tenant-desc": "Use current tenant as new originator.", - "originator-related-entity-desc": "Use related entity as new originator. Lookup based on configured relation type and direction.", - "originator-alarm-originator-desc": "Use alarm originator as new originator. Only if incoming message originator is alarm entity.", - "originator-entity-by-name-pattern-desc": "Use entity fetched from DB as new originator. Lookup based on entity type and specified name pattern.", - "email-from-template-hint": "Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", - "recipients-block-main-hint": "Comma-separated address list. All input fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", - "forward-msg-default-rule-chain": "Forward message to the originator's default rule chain", - "forward-msg-default-rule-chain-tooltip": "If enabled, message will be forwarded to the originator's default rule chain, or rule chain from configuration, if originator has no default rule chain defined in the entity profile.", - "exclude-zero-deltas": "Exclude zero deltas from outbound message", - "exclude-zero-deltas-hint": "If enabled, the \"{{outputValueKey}}\" output key will be added to the outbound message if its value is not zero.", - "exclude-zero-deltas-time-difference-hint": "If enabled, the \"{{outputValueKey}}\" and \"{{periodValueKey}}\" output keys will be added to the outbound message only if the \"{{outputValueKey}}\" value is not zero.", - "search-direction-from": "From originator to target entity", - "search-direction-to": "From target entity to originator", - "del-relation-direction-from": "From originator", - "del-relation-direction-to": "To originator", - "target-entity": "Target entity", - "function-configuration": "Function configuration", - "function-name": "Function name", - "function-name-required": "Function name is required.", - "qualifier": "Qualifier", - "qualifier-hint": "If the qualifier is not specified, the default qualifier \"$LATEST\" will be used.", - "aws-credentials": "AWS Credentials", - "connection-timeout": "Connection timeout", - "connection-timeout-required": "Connection timeout is required.", - "connection-timeout-min": "Min connection timeout is 0.", - "connection-timeout-hint": "The amount of time to wait in seconds when initially establishing a connection before giving up and timing out. A value of 0 means infinity, and is not recommended.", - "request-timeout": "Request timeout", - "request-timeout-required": "Request timeout is required", - "request-timeout-min": "Min request timeout is 0", - "request-timeout-hint": "The amount of time to wait in seconds for the request to complete before giving up and timing out. A value of 0 means infinity, and is not recommended.", - "tell-failure-aws-lambda": "Tell Failure if AWS Lambda function execution raises exception", - "tell-failure-aws-lambda-hint": "Rule node forces failure of message processing if AWS Lambda function execution raises exception." - }, - "key-val": { - "key": "Key", - "value": "Value", - "see-examples": "See examples.", - "remove-entry": "Remove entry", - "remove-mapping-entry": "Remove mapping entry", - "add-mapping-entry": "Add mapping", - "add-entry": "Add entry", - "copy-key-values-from": "Copy key-values from", - "delete-key-values": "Delete key-values", - "delete-key-values-from": "Delete key-values from", - "at-least-one-key-error": "At least one key should be selected.", - "unique-key-value-pair-error": "'{{keyText}}' must be different from the '{{valText}}'!" - }, - "mail-body-type": { - "plain-text": "Plain text", - "html": "HTML", - "dynamic": "Dynamic", - "use-body-type-template": "Use body type template", - "plain-text-description": "Simple, unformatted text with no special styling or formating.", - "html-text-description": "Allows you to use HTML tags for formatting, links and images in your mai body.", - "dynamic-text-description": "Allows to use Plain Text or HTML body type dynamically based on templatization feature.", - "after-template-evaluation-hint": "After template evaluation value should be true for HTML, and false for Plain text." - } - }, "tenant": { "tenant": "Tenant", "tenants": "Tenants", From d660f60b8273b24e8c83a21ad891856e6a49a234 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Tue, 14 Jan 2025 11:50:33 +0200 Subject: [PATCH 057/108] Fixed both import types active on import dialog open --- ui-ngx/src/app/shared/import-export/import-dialog.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/shared/import-export/import-dialog.component.ts b/ui-ngx/src/app/shared/import-export/import-dialog.component.ts index e4256229c4..7e46f47978 100644 --- a/ui-ngx/src/app/shared/import-export/import-dialog.component.ts +++ b/ui-ngx/src/app/shared/import-export/import-dialog.component.ts @@ -71,7 +71,7 @@ export class ImportDialogComponent extends DialogComponent Date: Tue, 14 Jan 2025 12:43:47 +0200 Subject: [PATCH 058/108] UI: Remove rule node config map; Add new function extractComponentsFromModule --- .../src/app/core/http/rule-chain.service.ts | 4 +- .../app/core/services/resources.service.ts | 38 +++++++++++++++++- .../action/action-rule-node-config.module.ts | 26 ------------ .../enrichment-rule-node-core.module.ts | 13 ------ .../external-rule-node-config.module.ts | 16 -------- .../filter/filter-rule-node-config.module.ts | 11 ----- .../flow/flow-rule-node-config.module.ts | 5 --- .../rule-node/rule-node-config.module.ts | 40 ++++--------------- .../transformation-rule-node-config.module.ts | 11 ----- 9 files changed, 45 insertions(+), 119 deletions(-) diff --git a/ui-ngx/src/app/core/http/rule-chain.service.ts b/ui-ngx/src/app/core/http/rule-chain.service.ts index 37a9f8c1af..88bc4286dc 100644 --- a/ui-ngx/src/app/core/http/rule-chain.service.ts +++ b/ui-ngx/src/app/core/http/rule-chain.service.ts @@ -184,8 +184,8 @@ export class RuleChainService { return this.http.post(url, inputParams, defaultHttpOptionsFromConfig(config)); } - public registemSystemRuleNodeConfigComponent(componentMap: Record>) { - this.systemRuleNodeConfigComponents = componentMap; + public registerSystemRuleNodeConfigModule(module: any) { + this.systemRuleNodeConfigComponents = this.resourcesService.extractComponentsFromModule(module, true); } private loadRuleNodeComponents(ruleChainType: RuleChainType, config?: RequestConfig): Observable> { diff --git a/ui-ngx/src/app/core/services/resources.service.ts b/ui-ngx/src/app/core/services/resources.service.ts index f5cba07a47..38bb343aae 100644 --- a/ui-ngx/src/app/core/services/resources.service.ts +++ b/ui-ngx/src/app/core/services/resources.service.ts @@ -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[]; } +export type ComponentsSelectorMap = Record>; + 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): 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(module: any, isCamelCaseSelector = false): ComponentsSelectorMap { + const modulesWithComponents = this.extractModulesWithComponents(module); + const componentMap = {}; + + const processComponents = (components: Array<ɵComponentDef>) => { + 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) { diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/action-rule-node-config.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/action-rule-node-config.module.ts index 3e78ee4c46..17307cb341 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/action-rule-node-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/action-rule-node-config.module.ts @@ -103,29 +103,3 @@ import { SendRestApiCallReplyConfigComponent } from './send-rest-api-call-reply- }) export class ActionRuleNodeConfigModule { } - -export const actionRuleNodeConfigComponentsMap: Record> = { - 'tbActionNodeAssignToCustomerConfig': AssignCustomerConfigComponent, - 'tbActionNodeAttributesConfig': AttributesConfigComponent, - 'tbActionNodeClearAlarmConfig': ClearAlarmConfigComponent, - 'tbActionNodeCreateAlarmConfig': CreateAlarmConfigComponent, - 'tbActionNodeCreateRelationConfig': CreateRelationConfigComponent, - 'tbActionNodeDeleteAttributesConfig': DeleteAttributesConfigComponent, - 'tbActionNodeDeleteRelationConfig': DeleteRelationConfigComponent, - 'tbActionNodeDeviceProfileConfig': DeviceProfileConfigComponent, - 'tbActionNodeDeviceStateConfig': DeviceStateConfigComponent, - 'tbActionNodeGeneratorConfig': GeneratorConfigComponent, - 'tbActionNodeGpsGeofencingConfig': GpsGeoActionConfigComponent, - 'tbActionNodeLogConfig': LogConfigComponent, - 'tbActionNodeMathFunctionConfig': MathFunctionConfigComponent, - 'tbActionNodeMsgCountConfig': MsgCountConfigComponent, - 'tbActionNodeMsgDelayConfig': MsgDelayConfigComponent, - 'tbActionNodePushToCloudConfig': PushToCloudConfigComponent, - 'tbActionNodePushToEdgeConfig': PushToEdgeConfigComponent, - 'tbActionNodeRpcReplyConfig': RpcReplyConfigComponent, - 'tbActionNodeRpcRequestConfig': RpcRequestConfigComponent, - 'tbActionNodeCustomTableConfig': SaveToCustomTableConfigComponent, - 'tbActionNodeSendRestApiCallReplyConfig': SendRestApiCallReplyConfigComponent, - 'tbActionNodeTimeseriesConfig': TimeseriesConfigComponent, - 'tbActionNodeUnAssignToCustomerConfig': UnassignCustomerConfigComponent -}; diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/enrichment-rule-node-core.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/enrichment-rule-node-core.module.ts index 987389feb8..7d614ecd96 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/enrichment-rule-node-core.module.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/enrichment-rule-node-core.module.ts @@ -62,16 +62,3 @@ import { FetchDeviceCredentialsConfigComponent } from './fetch-device-credential }) export class EnrichmentRuleNodeCoreModule { } - -export const enrichmentRuleNodeConfigComponentsMap: Record> = { - 'tbEnrichmentNodeCalculateDeltaConfig': CalculateDeltaConfigComponent, - 'tbEnrichmentNodeCustomerAttributesConfig': CustomerAttributesConfigComponent, - 'tbEnrichmentNodeDeviceAttributesConfig': DeviceAttributesConfigComponent, - 'tbEnrichmentNodeEntityDetailsConfig': EntityDetailsConfigComponent, - 'tbEnrichmentNodeFetchDeviceCredentialsConfig': FetchDeviceCredentialsConfigComponent, - 'tbEnrichmentNodeGetTelemetryFromDatabase': GetTelemetryFromDatabaseConfigComponent, - 'tbEnrichmentNodeOriginatorAttributesConfig': OriginatorAttributesConfigComponent, - 'tbEnrichmentNodeOriginatorFieldsConfig': OriginatorFieldsConfigComponent, - 'tbEnrichmentNodeRelatedAttributesConfig': RelatedAttributesConfigComponent, - 'tbEnrichmentNodeTenantAttributesConfig': TenantAttributesConfigComponent -} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/external-rule-node-config.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/external/external-rule-node-config.module.ts index 38395b9dac..c3c8c2ca2c 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/external/external-rule-node-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/external-rule-node-config.module.ts @@ -73,19 +73,3 @@ import { LambdaConfigComponent } from './lambda-config.component'; }) export class ExternalRuleNodeConfigModule { } - -export const externalRuleNodeConfigComponentsMap: Record> = { - 'tbExternalNodeAzureIotHubConfig': AzureIotHubConfigComponent, - 'tbExternalNodeKafkaConfig': KafkaConfigComponent, - 'tbExternalNodeLambdaConfig': LambdaConfigComponent, - 'tbExternalNodeMqttConfig': MqttConfigComponent, - 'tbExternalNodeNotificationConfig': NotificationConfigComponent, - 'tbExternalNodePubSubConfig': PubSubConfigComponent, - 'tbExternalNodeRabbitMqConfig': RabbitMqConfigComponent, - 'tbExternalNodeRestApiCallConfig': RestApiCallConfigComponent, - 'tbExternalNodeSendEmailConfig': SendEmailConfigComponent, - 'tbExternalNodeSendSmsConfig': SendSmsConfigComponent, - 'tbExternalNodeSlackConfig': SlackConfigComponent, - 'tbExternalNodeSnsConfig': SnsConfigComponent, - 'tbExternalNodeSqsConfig': SqsConfigComponent -} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/filter-rule-node-config.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/filter/filter-rule-node-config.module.ts index ca0aaffd71..bb3636811b 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/filter/filter-rule-node-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/filter-rule-node-config.module.ts @@ -56,14 +56,3 @@ import { CommonRuleNodeConfigModule } from '../common/common-rule-node-config.mo }) export class FilterRuleNodeConfigModule { } - -export const filterRuleNodeConfigComponentsMap: Record> = { - 'tbFilterNodeCheckAlarmStatusConfig': CheckAlarmStatusComponent, - 'tbFilterNodeCheckMessageConfig': CheckMessageConfigComponent, - 'tbFilterNodeCheckRelationConfig': CheckRelationConfigComponent, - 'tbFilterNodeGpsGeofencingConfig': GpsGeoFilterConfigComponent, - 'tbFilterNodeMessageTypeConfig': MessageTypeConfigComponent, - 'tbFilterNodeOriginatorTypeConfig': OriginatorTypeConfigComponent, - 'tbFilterNodeScriptConfig': ScriptConfigComponent, - 'tbFilterNodeSwitchConfig': SwitchConfigComponent -} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/flow/flow-rule-node-config.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/flow/flow-rule-node-config.module.ts index 2ee36f71fc..8a703ca9d2 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/flow/flow-rule-node-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/flow/flow-rule-node-config.module.ts @@ -36,8 +36,3 @@ import { RuleChainOutputComponent } from './rule-chain-output.component'; }) export class FlowRuleNodeConfigModule { } - -export const flowRuleNodeConfigComponentsMap: Record> = { - 'tbFlowNodeRuleChainInputConfig': RuleChainInputComponent, - 'tbFlowNodeRuleChainOutputConfig': RuleChainOutputComponent -} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.module.ts index f960777220..86512eceea 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/rule-node-config.module.ts @@ -14,35 +14,18 @@ /// limitations under the License. /// -import { NgModule, Type } from '@angular/core'; +import { NgModule } from '@angular/core'; import { EmptyConfigComponent } from './empty-config.component'; import { CommonModule } from '@angular/common'; import { SharedModule } from '@shared/shared.module'; +import { ActionRuleNodeConfigModule } from '@home/components/rule-node/action/action-rule-node-config.module'; +import { FilterRuleNodeConfigModule } from '@home/components/rule-node/filter/filter-rule-node-config.module'; +import { EnrichmentRuleNodeCoreModule } from '@home/components/rule-node/enrichment/enrichment-rule-node-core.module'; +import { ExternalRuleNodeConfigModule } from '@home/components/rule-node/external/external-rule-node-config.module'; import { - actionRuleNodeConfigComponentsMap, - ActionRuleNodeConfigModule -} from '@home/components/rule-node/action/action-rule-node-config.module'; -import { - filterRuleNodeConfigComponentsMap, - FilterRuleNodeConfigModule -} from '@home/components/rule-node/filter/filter-rule-node-config.module'; -import { - enrichmentRuleNodeConfigComponentsMap, - EnrichmentRuleNodeCoreModule -} from '@home/components/rule-node/enrichment/enrichment-rule-node-core.module'; -import { - externalRuleNodeConfigComponentsMap, - ExternalRuleNodeConfigModule -} from '@home/components/rule-node/external/external-rule-node-config.module'; -import { - transformationRuleNodeConfigComponentsMap, TransformationRuleNodeConfigModule } from '@home/components/rule-node/transformation/transformation-rule-node-config.module'; -import { - flowRuleNodeConfigComponentsMap, - FlowRuleNodeConfigModule -} from '@home/components/rule-node/flow/flow-rule-node-config.module'; -import { IRuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; +import { FlowRuleNodeConfigModule } from '@home/components/rule-node/flow/flow-rule-node-config.module'; import { RuleChainService } from '@core/http/rule-chain.service'; @NgModule({ @@ -65,15 +48,6 @@ import { RuleChainService } from '@core/http/rule-chain.service'; }) export class RuleNodeConfigModule { constructor(private ruleChainService: RuleChainService) { - const ruleNodeConfigComponentsMap: Record> = { - ...actionRuleNodeConfigComponentsMap, - ...enrichmentRuleNodeConfigComponentsMap, - ...externalRuleNodeConfigComponentsMap, - ...filterRuleNodeConfigComponentsMap, - ...flowRuleNodeConfigComponentsMap, - ...transformationRuleNodeConfigComponentsMap, - 'tbNodeEmptyConfig': EmptyConfigComponent - }; - this.ruleChainService.registemSystemRuleNodeConfigComponent(ruleNodeConfigComponentsMap); + this.ruleChainService.registerSystemRuleNodeConfigModule(this.constructor); } } diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transformation/transformation-rule-node-config.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/transformation/transformation-rule-node-config.module.ts index a25495d7b4..76ba7934d6 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/transformation/transformation-rule-node-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/transformation/transformation-rule-node-config.module.ts @@ -57,14 +57,3 @@ import { ScriptConfigComponent } from '@home/components/rule-node/filter/script- }) export class TransformationRuleNodeConfigModule { } - -export const transformationRuleNodeConfigComponentsMap: Record> = { - 'tbTransformationNodeChangeOriginatorConfig': ChangeOriginatorConfigComponent, - 'tbTransformationNodeCopyKeysConfig': CopyKeysConfigComponent, - 'tbTransformationNodeDeduplicationConfig': DeduplicationConfigComponent, - 'tbTransformationNodeDeleteKeysConfig': DeleteKeysConfigComponent, - 'tbTransformationNodeJsonPathConfig': NodeJsonPathConfigComponent, - 'tbTransformationNodeRenameKeysConfig': RenameKeysConfigComponent, - 'tbTransformationNodeScriptConfig': ScriptConfigComponent, - 'tbTransformationNodeToEmailConfig': ToEmailConfigComponent -} From 82e8033bd1a7721aa1b88d09b14b49f15f061e86 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Tue, 14 Jan 2025 12:43:54 +0200 Subject: [PATCH 059/108] Fixed columns to display filter panel scroll when on dashboard bottom edge --- .../components/widget/lib/display-columns-panel.component.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/display-columns-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/display-columns-panel.component.scss index 1a0ce86b89..1f92b714a9 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/display-columns-panel.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/display-columns-panel.component.scss @@ -15,7 +15,6 @@ */ :host { width: 100%; - height: 100%; min-width: 300px; overflow: auto; background: #fff; From 192f6b4e152cd33b78d1e9c49ea529bb3743d8cd Mon Sep 17 00:00:00 2001 From: mpetrov Date: Tue, 14 Jan 2025 13:38:59 +0200 Subject: [PATCH 060/108] Reverted custom translate for confirm dialog --- .../app/shared/components/dialog/confirm-dialog.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/shared/components/dialog/confirm-dialog.component.html b/ui-ngx/src/app/shared/components/dialog/confirm-dialog.component.html index 46d90f5ce0..c9bca14480 100644 --- a/ui-ngx/src/app/shared/components/dialog/confirm-dialog.component.html +++ b/ui-ngx/src/app/shared/components/dialog/confirm-dialog.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -

    {{data.title | customTranslate}}

    +

    {{data.title}}

    From d1acba40a29391d59df1b5e96359664be1b503ea Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Tue, 14 Jan 2025 15:20:16 +0200 Subject: [PATCH 061/108] Save time series strategies: add tests for changes in telemetry service --- .../DefaultTbEntityViewService.java | 1 + .../DefaultTelemetrySubscriptionService.java | 7 +- ...faultTelemetrySubscriptionServiceTest.java | 387 ++++++++++++++++++ .../engine/api/TimeseriesSaveRequestTest.java | 33 ++ 4 files changed, 425 insertions(+), 3 deletions(-) create mode 100644 application/src/test/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionServiceTest.java create mode 100644 rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequestTest.java diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java index 8e0a901eed..ee807da750 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java @@ -350,6 +350,7 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen .entries(latestValues) .saveTimeseries(false) .saveLatest(true) + .sendWsUpdate(true) .callback(new FutureCallback() { @Override public void onSuccess(@Nullable Void tmp) { diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java index d7ff5fa5b7..dbd7967989 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java @@ -149,8 +149,8 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer if (request.isSendWsUpdate()) { addWsCallback(saveFuture, success -> onTimeSeriesUpdate(tenantId, entityId, request.getEntries())); } - if (request.isSaveTimeseries() && request.isSaveLatest()) { - addEntityViewCallback(tenantId, entityId, request.getEntries()); + if (request.isSaveLatest()) { + copyLatestToEntityViews(tenantId, entityId, request.getEntries()); } return saveFuture; } @@ -205,7 +205,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer } } - private void addEntityViewCallback(TenantId tenantId, EntityId entityId, List ts) { + private void copyLatestToEntityViews(TenantId tenantId, EntityId entityId, List ts) { if (EntityType.DEVICE.equals(entityId.getEntityType()) || EntityType.ASSET.equals(entityId.getEntityType())) { Futures.addCallback(this.tbEntityViewService.findEntityViewsByTenantIdAndEntityIdAsync(tenantId, entityId), new FutureCallback<>() { @@ -238,6 +238,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer .entries(entityViewLatest) .saveTimeseries(false) .saveLatest(true) + .sendWsUpdate(true) .callback(new FutureCallback<>() { @Override public void onSuccess(@Nullable Void tmp) {} diff --git a/application/src/test/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionServiceTest.java b/application/src/test/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionServiceTest.java new file mode 100644 index 0000000000..8162ef4555 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionServiceTest.java @@ -0,0 +1,387 @@ +/** + * 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. + */ +package org.thingsboard.server.service.telemetry; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.common.util.concurrent.SettableFuture; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.util.ReflectionTestUtils; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; +import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.data.ApiUsageRecordKey; +import org.thingsboard.server.common.data.ApiUsageState; +import org.thingsboard.server.common.data.ApiUsageStateValue; +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityViewId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.DoubleDataEntry; +import org.thingsboard.server.common.data.kv.KvEntry; +import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.objects.AttributesEntityView; +import org.thingsboard.server.common.data.objects.TelemetryEntityView; +import org.thingsboard.server.common.msg.queue.ServiceType; +import org.thingsboard.server.common.msg.queue.TbCallback; +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; +import org.thingsboard.server.common.stats.TbApiUsageReportClient; +import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.timeseries.TimeseriesService; +import org.thingsboard.server.queue.discovery.PartitionService; +import org.thingsboard.server.queue.discovery.QueueKey; +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; +import org.thingsboard.server.service.apiusage.TbApiUsageStateService; +import org.thingsboard.server.service.entitiy.entityview.TbEntityViewService; +import org.thingsboard.server.service.subscription.SubscriptionManagerService; + +import java.time.Duration; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +import static com.google.common.util.concurrent.Futures.immediateFuture; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.lenient; + +@ExtendWith(MockitoExtension.class) +class DefaultTelemetrySubscriptionServiceTest { + + final TenantId tenantId = TenantId.fromUUID(UUID.fromString("a00ec470-c6b4-11ef-8c88-63b5533fb5bc")); + final CustomerId customerId = new CustomerId(UUID.fromString("7bdc9750-c775-11ef-8e03-ff69ed8da327")); + final EntityId entityId = DeviceId.fromString("cc51e450-53e1-11ee-883e-e56b48fd2088"); + + final long sampleTtl = 10_000L; + + final List sampleTelemetry = List.of( + new BasicTsKvEntry(100L, new DoubleDataEntry("temperature", 65.2)), + new BasicTsKvEntry(100L, new DoubleDataEntry("humidity", 33.1)) + ); + + ApiUsageState apiUsageState; + + final TopicPartitionInfo tpi = TopicPartitionInfo.builder() + .tenantId(tenantId) + .myPartition(true) + .build(); + + final FutureCallback emptyCallback = new FutureCallback<>() { + @Override + public void onSuccess(Void result) {} + + @Override + public void onFailure(@NonNull Throwable t) {} + }; + + ExecutorService wsCallBackExecutor; + ExecutorService tsCallBackExecutor; + + @Mock + TbClusterService clusterService; + @Mock + PartitionService partitionService; + @Mock + SubscriptionManagerService subscriptionManagerService; + @Mock + AttributesService attrService; + @Mock + TimeseriesService tsService; + @Mock + TbEntityViewService tbEntityViewService; + @Mock + TbApiUsageReportClient apiUsageClient; + @Mock + TbApiUsageStateService apiUsageStateService; + + DefaultTelemetrySubscriptionService telemetryService; + + @BeforeEach + void setup() { + telemetryService = new DefaultTelemetrySubscriptionService(attrService, tsService, tbEntityViewService, apiUsageClient, apiUsageStateService); + ReflectionTestUtils.setField(telemetryService, "clusterService", clusterService); + ReflectionTestUtils.setField(telemetryService, "partitionService", partitionService); + ReflectionTestUtils.setField(telemetryService, "subscriptionManagerService", Optional.of(subscriptionManagerService)); + + wsCallBackExecutor = MoreExecutors.newDirectExecutorService(); + ReflectionTestUtils.setField(telemetryService, "wsCallBackExecutor", wsCallBackExecutor); + + tsCallBackExecutor = MoreExecutors.newDirectExecutorService(); + ReflectionTestUtils.setField(telemetryService, "tsCallBackExecutor", tsCallBackExecutor); + + apiUsageState = new ApiUsageState(); + apiUsageState.setDbStorageState(ApiUsageStateValue.ENABLED); + lenient().when(apiUsageStateService.getApiUsageState(tenantId)).thenReturn(apiUsageState); + + lenient().when(partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId)).thenReturn(tpi); + + lenient().when(tsService.save(tenantId, entityId, sampleTelemetry, sampleTtl)).thenReturn(immediateFuture(sampleTelemetry.size())); + lenient().when(tsService.saveWithoutLatest(tenantId, entityId, sampleTelemetry, sampleTtl)).thenReturn(immediateFuture(sampleTelemetry.size())); + lenient().when(tsService.saveLatest(tenantId, entityId, sampleTelemetry)).thenReturn(immediateFuture(listOfNNumbers(sampleTelemetry.size()))); + + // mock no entity views + lenient().when(tbEntityViewService.findEntityViewsByTenantIdAndEntityIdAsync(tenantId, entityId)).thenReturn(immediateFuture(Collections.emptyList())); + + // send partition change event so currentPartitions set is populated + telemetryService.onTbApplicationEvent(new PartitionChangeEvent(this, ServiceType.TB_CORE, Map.of(new QueueKey(ServiceType.TB_CORE), Set.of(tpi)))); + } + + @AfterEach + void cleanup() { + wsCallBackExecutor.shutdownNow(); + tsCallBackExecutor.shutdownNow(); + } + + @Test + void shouldReportStorageDataPointsApiUsageWhenTimeSeriesIsSaved() { + // GIVEN + var request = TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .customerId(customerId) + .entityId(entityId) + .entries(sampleTelemetry) + .ttl(sampleTtl) + .saveTimeseries(true) + .saveLatest(false) + .sendWsUpdate(false) + .callback(emptyCallback) + .build(); + + // WHEN + telemetryService.saveTimeseries(request); + + // THEN + then(apiUsageClient).should().report(tenantId, customerId, ApiUsageRecordKey.STORAGE_DP_COUNT, sampleTelemetry.size()); + } + + @Test + void shouldNotReportStorageDataPointsApiUsageWhenTimeSeriesIsNotSaved() { + // GIVEN + var request = TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .customerId(customerId) + .entityId(entityId) + .entries(sampleTelemetry) + .ttl(sampleTtl) + .saveTimeseries(false) + .saveLatest(true) + .sendWsUpdate(true) + .callback(emptyCallback) + .build(); + + // WHEN + telemetryService.saveTimeseries(request); + + // THEN + then(apiUsageClient).shouldHaveNoInteractions(); + } + + @Test + void shouldThrowStorageDisabledWhenTimeSeriesIsSavedAndStorageIsDisabled() { + // GIVEN + apiUsageState.setDbStorageState(ApiUsageStateValue.DISABLED); + + SettableFuture future = SettableFuture.create(); + var request = TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .customerId(customerId) + .entityId(entityId) + .entries(sampleTelemetry) + .ttl(sampleTtl) + .saveTimeseries(true) + .saveLatest(true) + .sendWsUpdate(true) + .future(future) + .build(); + + // WHEN + telemetryService.saveTimeseries(request); + + // THEN + assertThat(future).failsWithin(Duration.ofSeconds(5)) + .withThrowableOfType(ExecutionException.class) + .withCauseInstanceOf(RuntimeException.class) + .withMessageContaining("DB storage writes are disabled due to API limits!"); + } + + @Test + void shouldNotThrowStorageDisabledWhenTimeSeriesIsNotSavedAndStorageIsDisabled() { + // GIVEN + apiUsageState.setDbStorageState(ApiUsageStateValue.DISABLED); + + SettableFuture future = SettableFuture.create(); + var request = TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .customerId(customerId) + .entityId(entityId) + .entries(sampleTelemetry) + .ttl(sampleTtl) + .saveTimeseries(false) + .saveLatest(true) + .sendWsUpdate(true) + .future(future) + .build(); + + // WHEN + telemetryService.saveTimeseries(request); + + // THEN + assertThat(future).succeedsWithin(Duration.ofSeconds(5)); + } + + @Test + void shouldCopyLatestToEntityViewWhenLatestIsSavedOnMainEntity() { + // GIVEN + var entityView = new EntityView(new EntityViewId(UUID.randomUUID())); + entityView.setTenantId(tenantId); + entityView.setCustomerId(customerId); + entityView.setEntityId(entityId); + entityView.setKeys(new TelemetryEntityView(sampleTelemetry.stream().map(KvEntry::getKey).toList(), new AttributesEntityView())); + + // mock that there is one entity view + given(tbEntityViewService.findEntityViewsByTenantIdAndEntityIdAsync(tenantId, entityId)).willReturn(immediateFuture(List.of(entityView))); + // mock that save latest call for entity view is successful + given(tsService.saveLatest(tenantId, entityView.getId(), sampleTelemetry)).willReturn(immediateFuture(listOfNNumbers(sampleTelemetry.size()))); + // mock TPI for entity view + given(partitionService.resolve(ServiceType.TB_CORE, tenantId, entityView.getId())).willReturn(tpi); + + var request = TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .customerId(customerId) + .entityId(entityId) + .entries(sampleTelemetry) + .ttl(sampleTtl) + .saveTimeseries(false) + .saveLatest(true) + .sendWsUpdate(false) + .callback(emptyCallback) + .build(); + + // WHEN + telemetryService.saveTimeseries(request); + + // THEN + // should save latest to both the main entity and it's entity view + then(tsService).should().saveLatest(tenantId, entityId, sampleTelemetry); + then(tsService).should().saveLatest(tenantId, entityView.getId(), sampleTelemetry); + then(tsService).shouldHaveNoMoreInteractions(); + + // should send WS update only for entity view (WS update for the main entity is disabled in the save request) + then(subscriptionManagerService).should().onTimeSeriesUpdate(tenantId, entityView.getId(), sampleTelemetry, TbCallback.EMPTY); + then(subscriptionManagerService).shouldHaveNoMoreInteractions(); + } + + @Test + void shouldNotCopyLatestToEntityViewWhenLatestIsNotSavedOnMainEntity() { + // GIVEN + var request = TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .customerId(customerId) + .entityId(entityId) + .entries(sampleTelemetry) + .ttl(sampleTtl) + .saveTimeseries(true) + .saveLatest(false) + .sendWsUpdate(false) + .callback(emptyCallback) + .build(); + + // WHEN + telemetryService.saveTimeseries(request); + + // THEN + // should save only time series for the main entity + then(tsService).should().saveWithoutLatest(tenantId, entityId, sampleTelemetry, sampleTtl); + then(tsService).shouldHaveNoMoreInteractions(); + + // should not send any WS updates + then(subscriptionManagerService).shouldHaveNoInteractions(); + } + + @ParameterizedTest + @MethodSource("booleanCombinations") + void shouldCallCorrectApiBasedOnBooleanFlagsInTheSaveRequest(boolean saveTimeseries, boolean saveLatest, boolean sendWsUpdate) { + // GIVEN + var request = TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .customerId(customerId) + .entityId(entityId) + .entries(sampleTelemetry) + .ttl(sampleTtl) + .saveTimeseries(saveTimeseries) + .saveLatest(saveLatest) + .sendWsUpdate(sendWsUpdate) + .callback(emptyCallback) + .build(); + + // WHEN + telemetryService.saveTimeseries(request); + + // THEN + if (saveTimeseries && saveLatest) { + then(tsService).should().save(tenantId, entityId, sampleTelemetry, sampleTtl); + } else if (saveLatest) { + then(tsService).should().saveLatest(tenantId, entityId, sampleTelemetry); + } else if (saveTimeseries) { + then(tsService).should().saveWithoutLatest(tenantId, entityId, sampleTelemetry, sampleTtl); + } + then(tsService).shouldHaveNoMoreInteractions(); + + if (sendWsUpdate) { + then(subscriptionManagerService).should().onTimeSeriesUpdate(tenantId, entityId, sampleTelemetry, TbCallback.EMPTY); + } else { + then(subscriptionManagerService).shouldHaveNoInteractions(); + } + } + + private static Stream booleanCombinations() { + return Stream.of( + Arguments.of(true, true, true), + Arguments.of(true, true, false), + Arguments.of(true, false, true), + Arguments.of(true, false, false), + Arguments.of(false, true, true), + Arguments.of(false, true, false), + Arguments.of(false, false, true), + Arguments.of(false, false, false) + ); + } + + // used to emulate sequence numbers returned by save latest API + private static List listOfNNumbers(int N) { + return LongStream.range(0, N).boxed().toList(); + } + +} diff --git a/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequestTest.java b/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequestTest.java new file mode 100644 index 0000000000..cf05f9905b --- /dev/null +++ b/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequestTest.java @@ -0,0 +1,33 @@ +/** + * 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. + */ +package org.thingsboard.rule.engine.api; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class TimeseriesSaveRequestTest { + + @Test + void testBooleanFlagsDefaultToTrue() { + var request = TimeseriesSaveRequest.builder().build(); + + assertThat(request.isSaveTimeseries()).isTrue(); + assertThat(request.isSaveLatest()).isTrue(); + assertThat(request.isSendWsUpdate()).isTrue(); + } + +} From 817b83c7bdb0d007494e94a27e40ebfb82d72705 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Tue, 14 Jan 2025 15:52:35 +0200 Subject: [PATCH 062/108] Added domain update request chaining --- .../domains/domain-table-config.resolver.ts | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain-table-config.resolver.ts index 8e1014a1da..95a260f9ba 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain-table-config.resolver.ts @@ -32,7 +32,8 @@ import { DomainComponent } from '@home/pages/admin/oauth2/domains/domain.compone import { isEqual } from '@core/utils'; import { DomainTableHeaderComponent } from '@home/pages/admin/oauth2/domains/domain-table-header.component'; import { Direction } from '@app/shared/models/page/sort-order'; -import { map, mergeMap, Observable, of } from 'rxjs'; +import { map, of } from 'rxjs'; +import { switchMap } from 'rxjs/operators'; @Injectable() export class DomainTableConfigResolver { @@ -84,16 +85,16 @@ export class DomainTableConfigResolver { this.config.loadEntity = id => this.domainService.getDomainInfoById(id.id); this.config.saveEntity = (domain, originalDomain) => { const clientsIds = domain.oauth2ClientInfos as Array || []; - let clientsTask: Observable; - if (domain.id && !isEqual(domain.oauth2ClientInfos?.sort(), - originalDomain.oauth2ClientInfos?.map(info => info.id ? info.id.id : info).sort())) { - clientsTask = this.domainService.updateOauth2Clients(domain.id.id, clientsIds); - } else { - clientsTask = of(null); - } delete domain.oauth2ClientInfos; - return clientsTask.pipe( - mergeMap(() => this.domainService.saveDomain(domain, domain.id ? [] : clientsIds)), + + return this.domainService.saveDomain(domain, domain.id ? [] : clientsIds).pipe( + switchMap(savedDomain => { + const shouldUpdateClients = domain.id && !isEqual(domain.oauth2ClientInfos?.sort(), + originalDomain.oauth2ClientInfos?.map(info => info.id ? info.id.id : info).sort()); + return shouldUpdateClients + ? this.domainService.updateOauth2Clients(domain.id.id, clientsIds).pipe(map(() => savedDomain)) + : of(savedDomain); + }), map(savedDomain => { (savedDomain as DomainInfo).oauth2ClientInfos = clientsIds; return savedDomain; From a5fa699c458e744e370c39642364298b64595f67 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Tue, 14 Jan 2025 15:57:49 +0200 Subject: [PATCH 063/108] Save domain refactoring --- ui-ngx/src/app/core/http/domain.service.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/core/http/domain.service.ts b/ui-ngx/src/app/core/http/domain.service.ts index bb6cd4a16b..f20e30d1b1 100644 --- a/ui-ngx/src/app/core/http/domain.service.ts +++ b/ui-ngx/src/app/core/http/domain.service.ts @@ -33,8 +33,11 @@ export class DomainService { } public saveDomain(domain: Domain, oauth2ClientIds: Array, config?: RequestConfig): Observable { - return this.http.post(`/api/domain?oauth2ClientIds=${oauth2ClientIds.join(',')}`, - domain, defaultHttpOptionsFromConfig(config)); + let url = '/api/domain'; + if (oauth2ClientIds?.length) { + url += `?oauth2ClientIds=${oauth2ClientIds.join(',')}`; + } + return this.http.post(url, domain, defaultHttpOptionsFromConfig(config)); } public updateOauth2Clients(id: string, oauth2ClientIds: Array, config?: RequestConfig): Observable { From 48d61db4f5218d9d285fd32d561a8a2b7987b7ba Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Tue, 14 Jan 2025 17:28:38 +0200 Subject: [PATCH 064/108] Set Edge Client version to 4.0.0 --- .../src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java | 2 +- common/edge-api/src/main/proto/edge.proto | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java b/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java index fcef33b968..3936b3ee63 100644 --- a/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java +++ b/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java @@ -136,7 +136,7 @@ public class EdgeGrpcClient implements EdgeRpcClient { .setConnectRequestMsg(ConnectRequestMsg.newBuilder() .setEdgeRoutingKey(edgeKey) .setEdgeSecret(edgeSecret) - .setEdgeVersion(EdgeVersion.V_3_9_0) + .setEdgeVersion(EdgeVersion.V_4_0_0) .setMaxInboundMessageSize(maxInboundMessageSize) .build()) .build()); diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index 4dc762e14c..50ecc31c1a 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -41,6 +41,7 @@ enum EdgeVersion { V_3_7_0 = 7; V_3_8_0 = 8; V_3_9_0 = 9; + V_4_0_0 = 10; } /** From 042ab7e807327117ca1453c0bdff81afdf076360 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 15 Jan 2025 17:20:05 +0200 Subject: [PATCH 065/108] UI: Add tailwinds constants from rule node config --- ui-ngx/tailwind.config.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui-ngx/tailwind.config.js b/ui-ngx/tailwind.config.js index e53b102710..b71b4eb235 100644 --- a/ui-ngx/tailwind.config.js +++ b/ui-ngx/tailwind.config.js @@ -93,6 +93,7 @@ module.exports = { '0.75': '0.1875rem', '1.25': '0.3125rem', '3.75': '0.9375rem', + '5.5': '1.375rem', '6.25': '1.5625rem' }, minHeight: { @@ -152,6 +153,7 @@ module.exports = { '7.5': '1.875rem', '25': '6.25rem', '37.5': '9.375rem', + '50': '12.5rem', '62.5': '15.625rem', '72.5': '18.125rem' }, From cad35a969e3db4894554f24cb72797b0356ba621 Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Wed, 15 Jan 2025 17:48:53 +0200 Subject: [PATCH 066/108] Save time series strategies: add tests for persistence strategies --- .../DeduplicatePersistenceStrategyTest.java | 147 ++++++++++++++++++ ...OnEveryMessagePersistenceStrategyTest.java | 48 ++++++ .../strategy/PersistenceStrategyTest.java | 55 +++++++ .../strategy/SkipPersistenceStrategyTest.java | 48 ++++++ 4 files changed, 298 insertions(+) create mode 100644 rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategyTest.java create mode 100644 rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/OnEveryMessagePersistenceStrategyTest.java create mode 100644 rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/PersistenceStrategyTest.java create mode 100644 rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/SkipPersistenceStrategyTest.java diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategyTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategyTest.java new file mode 100644 index 0000000000..3f57a7f3df --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategyTest.java @@ -0,0 +1,147 @@ +/** + * 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. + */ +package org.thingsboard.rule.engine.telemetry.strategy; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.time.Duration; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class DeduplicatePersistenceStrategyTest { + + final int deduplicationIntervalSecs = 10; + + DeduplicatePersistenceStrategy strategy; + + @BeforeEach + void setup() { + strategy = new DeduplicatePersistenceStrategy(deduplicationIntervalSecs); + } + + @Test + void shouldThrowWhenDeduplicationIntervalIsLessThanOneSecond() { + assertThatThrownBy(() -> new DeduplicatePersistenceStrategy(0)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Deduplication interval must be at least 1 second(s), was 0 second(s)"); + } + + @Test + void shouldReturnTrueForFirstCallInInterval() { + long ts = 1_000_000L; + UUID originator = UUID.randomUUID(); + + assertThat(strategy.shouldPersist(ts, originator)).isTrue(); + } + + @Test + void shouldReturnFalseForSubsequentCallsInInterval() { + long baseTs = 1_000_000L; + UUID originator = UUID.randomUUID(); + + // Initial call should return true + assertThat(strategy.shouldPersist(baseTs, originator)).isTrue(); + + // Subsequent call within the same interval should return false for the same originator + long withinSameIntervalTs = baseTs + 1000L; + assertThat(strategy.shouldPersist(withinSameIntervalTs, originator)).isFalse(); + } + + @Test + void shouldHandleMultipleOriginatorsIndependently() { + long baseTs = 1_000_000L; + UUID originator1 = UUID.randomUUID(); + UUID originator2 = UUID.randomUUID(); + + // First call for different originators in the same interval should return true independently + assertThat(strategy.shouldPersist(baseTs, originator1)).isTrue(); + assertThat(strategy.shouldPersist(baseTs, originator2)).isTrue(); + + // Subsequent calls for the same originators within the same interval should return false + assertThat(strategy.shouldPersist(baseTs + 500L, originator1)).isFalse(); + assertThat(strategy.shouldPersist(baseTs + 500L, originator2)).isFalse(); + } + + @Test + void shouldHandleEdgeCaseTimestamps() { + long minTs = Long.MIN_VALUE; + long maxTs = Long.MAX_VALUE; + UUID originator = UUID.randomUUID(); + + assertThat(strategy.shouldPersist(minTs, originator)).isTrue(); + assertThat(strategy.shouldPersist(minTs + 1L, originator)).isFalse(); + + assertThat(strategy.shouldPersist(maxTs, originator)).isTrue(); + assertThat(strategy.shouldPersist(maxTs - 1L, originator)).isFalse(); + } + + @Test + void shouldResetDeduplicationAtIntervalBoundaries() { + UUID originator = UUID.randomUUID(); + + // check 1st interval + long firstIntervalStart = 0L; + long firstIntervalEnd = firstIntervalStart + Duration.ofSeconds(deduplicationIntervalSecs).toMillis() - 1L; + long firstIntervalMiddle = calculateMiddle(firstIntervalStart, firstIntervalEnd); + + assertThat(strategy.shouldPersist(firstIntervalStart, originator)).isTrue(); + assertThat(strategy.shouldPersist(firstIntervalStart + 1, originator)).isFalse(); + assertThat(strategy.shouldPersist(firstIntervalMiddle, originator)).isFalse(); + assertThat(strategy.shouldPersist(firstIntervalEnd - 1, originator)).isFalse(); + assertThat(strategy.shouldPersist(firstIntervalEnd, originator)).isFalse(); + + // check 2nd interval + long secondIntervalStart = firstIntervalEnd + 1L; + long secondIntervalEnd = secondIntervalStart + Duration.ofSeconds(deduplicationIntervalSecs).toMillis() - 1L; + long secondIntervalMiddle = calculateMiddle(secondIntervalStart, secondIntervalEnd); + + assertThat(strategy.shouldPersist(secondIntervalStart, originator)).isTrue(); + assertThat(strategy.shouldPersist(secondIntervalStart + 1, originator)).isFalse(); + assertThat(strategy.shouldPersist(secondIntervalMiddle, originator)).isFalse(); + assertThat(strategy.shouldPersist(secondIntervalEnd - 1, originator)).isFalse(); + assertThat(strategy.shouldPersist(secondIntervalEnd, originator)).isFalse(); + } + + @Test + void shouldHandleMultipleOriginatorsOverMultipleIntervals() { + UUID originator1 = UUID.randomUUID(); + UUID originator2 = UUID.randomUUID(); + long baseTs = 0L; + + // First interval for both originators + assertThat(strategy.shouldPersist(baseTs, originator1)).isTrue(); + assertThat(strategy.shouldPersist(baseTs, originator2)).isTrue(); + + // Move to the next interval + long nextIntervalTs = baseTs + Duration.ofSeconds(10).toMillis(); + + // Each originator should be allowed again in the new interval + assertThat(strategy.shouldPersist(nextIntervalTs, originator1)).isTrue(); + assertThat(strategy.shouldPersist(nextIntervalTs, originator2)).isTrue(); + + // Subsequent calls in the same new interval should return false + assertThat(strategy.shouldPersist(nextIntervalTs + 500L, originator1)).isFalse(); + assertThat(strategy.shouldPersist(nextIntervalTs + 500L, originator2)).isFalse(); + } + + private static long calculateMiddle(long start, long end) { + return start + (end - start) / 2; + } + +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/OnEveryMessagePersistenceStrategyTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/OnEveryMessagePersistenceStrategyTest.java new file mode 100644 index 0000000000..125da3a495 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/OnEveryMessagePersistenceStrategyTest.java @@ -0,0 +1,48 @@ +/** + * 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. + */ +package org.thingsboard.rule.engine.telemetry.strategy; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.UUID; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +class OnEveryMessagePersistenceStrategyTest { + + @ParameterizedTest + @MethodSource("edgeCaseProvider") + void shouldAlwaysReturnTrueForAnyInput(long timestamp, UUID originator) { + var onEveryMessage = OnEveryMessagePersistenceStrategy.getInstance(); + assertThat(onEveryMessage.shouldPersist(timestamp, originator)).isTrue(); + } + + private static Stream edgeCaseProvider() { + return Stream.of( + Arguments.of(Long.MIN_VALUE, new UUID(0L, 0L)), + Arguments.of(Long.MAX_VALUE, new UUID(Long.MAX_VALUE, Long.MAX_VALUE)), + Arguments.of(0L, new UUID(0L, 0L)), + Arguments.of(-1L, new UUID(-1L, -1L)), + Arguments.of(1L, new UUID(1L, 1L)), + Arguments.of(42L, UUID.randomUUID()), + Arguments.of(1000L, null) + ); + } + +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/PersistenceStrategyTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/PersistenceStrategyTest.java new file mode 100644 index 0000000000..71a8eabed5 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/PersistenceStrategyTest.java @@ -0,0 +1,55 @@ +/** + * 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. + */ +package org.thingsboard.rule.engine.telemetry.strategy; + +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; + +class PersistenceStrategyTest { + + @Test + void testOnEveryMessageReturnsCorrectInstance() { + PersistenceStrategy strategy = PersistenceStrategy.onEveryMessage(); + assertThat(strategy) + .isNotNull() + .isInstanceOf(OnEveryMessagePersistenceStrategy.class); + } + + @Test + void testDeduplicateReturnsCorrectInstance() { + int validDeduplicationIntervalSecs = 5; + PersistenceStrategy strategy = PersistenceStrategy.deduplicate(validDeduplicationIntervalSecs); + assertThat(strategy) + .isNotNull() + .isInstanceOf(DeduplicatePersistenceStrategy.class); + + long actualDeduplicationIntervalMillis = (long) ReflectionTestUtils.getField(strategy, "deduplicationIntervalMillis"); + assertThat(actualDeduplicationIntervalMillis).isEqualTo(Duration.ofSeconds(validDeduplicationIntervalSecs).toMillis()); + } + + @Test + void testSkipReturnsCorrectInstance() { + PersistenceStrategy strategy = PersistenceStrategy.skip(); + assertThat(strategy) + .isNotNull() + .isInstanceOf(SkipPersistenceStrategy.class); + } + +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/SkipPersistenceStrategyTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/SkipPersistenceStrategyTest.java new file mode 100644 index 0000000000..1a63ce7460 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/SkipPersistenceStrategyTest.java @@ -0,0 +1,48 @@ +/** + * 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. + */ +package org.thingsboard.rule.engine.telemetry.strategy; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.UUID; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +class SkipPersistenceStrategyTest { + + @ParameterizedTest + @MethodSource("edgeCaseProvider") + void shouldAlwaysReturnFalseForAnyInput(long timestamp, UUID originator) { + var skipStrategy = SkipPersistenceStrategy.getInstance(); + assertThat(skipStrategy.shouldPersist(timestamp, originator)).isFalse(); + } + + private static Stream edgeCaseProvider() { + return Stream.of( + Arguments.of(Long.MIN_VALUE, new UUID(0L, 0L)), + Arguments.of(Long.MAX_VALUE, new UUID(Long.MAX_VALUE, Long.MAX_VALUE)), + Arguments.of(0L, new UUID(0L, 0L)), + Arguments.of(-1L, new UUID(-1L, -1L)), + Arguments.of(1L, new UUID(1L, 1L)), + Arguments.of(42L, UUID.randomUUID()), + Arguments.of(1000L, null) + ); + } + +} From f8dd68226c01fa5f5db6ead7806ae4d49bcf9d51 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Thu, 16 Jan 2025 12:01:07 +0200 Subject: [PATCH 067/108] resolved comments --- ui-ngx/src/app/core/http/domain.service.ts | 2 +- .../pages/admin/oauth2/domains/domain-table-config.resolver.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/core/http/domain.service.ts b/ui-ngx/src/app/core/http/domain.service.ts index f20e30d1b1..fe05305c6f 100644 --- a/ui-ngx/src/app/core/http/domain.service.ts +++ b/ui-ngx/src/app/core/http/domain.service.ts @@ -32,7 +32,7 @@ export class DomainService { ) { } - public saveDomain(domain: Domain, oauth2ClientIds: Array, config?: RequestConfig): Observable { + public saveDomain(domain: Domain, oauth2ClientIds?: Array, config?: RequestConfig): Observable { let url = '/api/domain'; if (oauth2ClientIds?.length) { url += `?oauth2ClientIds=${oauth2ClientIds.join(',')}`; diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain-table-config.resolver.ts index 95a260f9ba..4f0603545f 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain-table-config.resolver.ts @@ -85,11 +85,12 @@ export class DomainTableConfigResolver { this.config.loadEntity = id => this.domainService.getDomainInfoById(id.id); this.config.saveEntity = (domain, originalDomain) => { const clientsIds = domain.oauth2ClientInfos as Array || []; + const newDomainClients = domain.oauth2ClientInfos; delete domain.oauth2ClientInfos; return this.domainService.saveDomain(domain, domain.id ? [] : clientsIds).pipe( switchMap(savedDomain => { - const shouldUpdateClients = domain.id && !isEqual(domain.oauth2ClientInfos?.sort(), + const shouldUpdateClients = domain.id && !isEqual(newDomainClients?.sort(), originalDomain.oauth2ClientInfos?.map(info => info.id ? info.id.id : info).sort()); return shouldUpdateClients ? this.domainService.updateOauth2Clients(domain.id.id, clientsIds).pipe(map(() => savedDomain)) From 3aac81745f5d52f1d26ac2e679ed8d040e14f9cb Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Thu, 16 Jan 2025 12:58:22 +0200 Subject: [PATCH 068/108] Save time series strategies: add test for changes in entity view service --- .../DefaultTbEntityViewServiceTest.java | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 application/src/test/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewServiceTest.java diff --git a/application/src/test/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewServiceTest.java b/application/src/test/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewServiceTest.java new file mode 100644 index 0000000000..b36890f775 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewServiceTest.java @@ -0,0 +1,109 @@ +/** + * 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. + */ +package org.thingsboard.server.service.entitiy.entityview; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityViewId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.DoubleDataEntry; +import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.objects.AttributesEntityView; +import org.thingsboard.server.common.data.objects.TelemetryEntityView; +import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.entityview.EntityViewService; +import org.thingsboard.server.dao.timeseries.TimeseriesService; +import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; + +import java.util.List; +import java.util.UUID; + +import static com.google.common.util.concurrent.Futures.immediateFuture; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; + +@ExtendWith(MockitoExtension.class) +class DefaultTbEntityViewServiceTest { + + final TenantId tenantId = TenantId.fromUUID(UUID.fromString("f09c8180-686c-11ef-9471-a71d33080e9c")); + final EntityId entityId = DeviceId.fromString("782aaab0-c7a8-11ef-a668-79582e785d5f"); + + @Mock + EntityViewService entityViewService; + @Mock + AttributesService attributesService; + @Mock + TelemetrySubscriptionService tsSubService; + @Mock + TimeseriesService tsService; + + DefaultTbEntityViewService defaultTbEntityViewService; + + @BeforeEach + void setup() { + defaultTbEntityViewService = new DefaultTbEntityViewService(entityViewService, attributesService, tsSubService, tsService); + } + + @Test + void shouldNotSaveTimeseriesWhenCopyingLatestToEntityView() throws Exception { + // GIVEN + var entityView = new EntityView(new EntityViewId(UUID.randomUUID())); + entityView.setTenantId(tenantId); + entityView.setEntityId(entityId); + entityView.setKeys(new TelemetryEntityView(List.of("temperature"), new AttributesEntityView())); + + List latest = List.of(new BasicTsKvEntry(123L, new DoubleDataEntry("temperature", 22.3))); + + given(tsService.findAll(eq(tenantId), eq(entityId), anyList())).willReturn(immediateFuture(latest)); + + // WHEN + defaultTbEntityViewService.updateEntityViewAttributes(tenantId, entityView, null, null); + + // THEN + var captor = ArgumentCaptor.forClass(TimeseriesSaveRequest.class); + then(tsSubService).should().saveTimeseries(captor.capture()); + + var expectedCopyLatestRequest = TimeseriesSaveRequest.builder() + .tenantId(tenantId) + .entityId(entityView.getId()) + .entries(latest) + .ttl(0L) + .saveTimeseries(false) + .saveLatest(true) + .sendWsUpdate(true) + .build(); + + var actualCopyLatestRequest = captor.getValue(); + + assertThat(actualCopyLatestRequest) + .usingRecursiveComparison() + .ignoringFields("callback") + .isEqualTo(expectedCopyLatestRequest); + } + +} From 7edba5edb6ff29fde453660627362cb84d2861d3 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Thu, 16 Jan 2025 16:26:53 +0200 Subject: [PATCH 069/108] refactoring --- .../domains/domain-table-config.resolver.ts | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain-table-config.resolver.ts index 4f0603545f..64861285a8 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain-table-config.resolver.ts @@ -85,17 +85,15 @@ export class DomainTableConfigResolver { this.config.loadEntity = id => this.domainService.getDomainInfoById(id.id); this.config.saveEntity = (domain, originalDomain) => { const clientsIds = domain.oauth2ClientInfos as Array || []; - const newDomainClients = domain.oauth2ClientInfos; + const shouldUpdateClients = domain.id && !isEqual(domain.oauth2ClientInfos?.sort(), + originalDomain.oauth2ClientInfos?.map(info => info.id ? info.id.id : info).sort()); delete domain.oauth2ClientInfos; - return this.domainService.saveDomain(domain, domain.id ? [] : clientsIds).pipe( - switchMap(savedDomain => { - const shouldUpdateClients = domain.id && !isEqual(newDomainClients?.sort(), - originalDomain.oauth2ClientInfos?.map(info => info.id ? info.id.id : info).sort()); - return shouldUpdateClients - ? this.domainService.updateOauth2Clients(domain.id.id, clientsIds).pipe(map(() => savedDomain)) - : of(savedDomain); - }), + return this.domainService.saveDomain(domain, domain.id ? null : clientsIds).pipe( + switchMap(savedDomain => shouldUpdateClients + ? this.domainService.updateOauth2Clients(domain.id.id, clientsIds).pipe(map(() => savedDomain)) + : of(savedDomain) + ), map(savedDomain => { (savedDomain as DomainInfo).oauth2ClientInfos = clientsIds; return savedDomain; @@ -119,7 +117,7 @@ export class DomainTableConfigResolver { oauth2Enabled: !domain.oauth2Enabled }; - this.domainService.saveDomain(modifiedDomain, domain.oauth2ClientInfos.map(clientInfo => clientInfo.id.id), + this.domainService.saveDomain(modifiedDomain, null, {ignoreLoading: true}) .subscribe((result) => { domain.oauth2Enabled = result.oauth2Enabled; @@ -137,7 +135,7 @@ export class DomainTableConfigResolver { propagateToEdge: !domain.propagateToEdge }; - this.domainService.saveDomain(modifiedDomain, domain.oauth2ClientInfos.map(clientInfo => clientInfo.id.id), + this.domainService.saveDomain(modifiedDomain, null, {ignoreLoading: true}) .subscribe((result) => { domain.propagateToEdge = result.propagateToEdge; From 6cd2fdfe33fbbd09b92f75dc4f22a1b709393f46 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Thu, 16 Jan 2025 16:54:52 +0200 Subject: [PATCH 070/108] Removed oauth2ClientInfos for toggles --- .../oauth2/domains/domain-table-config.resolver.ts | 14 ++++---------- ui-ngx/src/app/shared/models/oauth2.models.ts | 6 +----- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain-table-config.resolver.ts index 64861285a8..8048fec885 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain-table-config.resolver.ts @@ -112,12 +112,9 @@ export class DomainTableConfigResolver { $event.stopPropagation(); } - const modifiedDomain: DomainInfo = { - ...domain, - oauth2Enabled: !domain.oauth2Enabled - }; + const { oauth2ClientInfos, oauth2Enabled, ...updatedDomain } = domain; - this.domainService.saveDomain(modifiedDomain, null, + this.domainService.saveDomain({ ...updatedDomain, oauth2Enabled: !oauth2Enabled }, null, {ignoreLoading: true}) .subscribe((result) => { domain.oauth2Enabled = result.oauth2Enabled; @@ -130,12 +127,9 @@ export class DomainTableConfigResolver { $event.stopPropagation(); } - const modifiedDomain: DomainInfo = { - ...domain, - propagateToEdge: !domain.propagateToEdge - }; + const { oauth2ClientInfos, propagateToEdge, ...updatedDomain } = domain; - this.domainService.saveDomain(modifiedDomain, null, + this.domainService.saveDomain({ ...updatedDomain, propagateToEdge: !propagateToEdge }, null, {ignoreLoading: true}) .subscribe((result) => { domain.propagateToEdge = result.propagateToEdge; diff --git a/ui-ngx/src/app/shared/models/oauth2.models.ts b/ui-ngx/src/app/shared/models/oauth2.models.ts index abc0ae1464..19a95a9788 100644 --- a/ui-ngx/src/app/shared/models/oauth2.models.ts +++ b/ui-ngx/src/app/shared/models/oauth2.models.ts @@ -80,11 +80,7 @@ export interface Domain extends BaseData, HasTenantId { propagateToEdge: boolean; } -export interface HasOauth2Clients { - oauth2ClientInfos?: Array | Array; -} - -export interface DomainInfo extends Domain, HasOauth2Clients { +export interface DomainInfo extends Domain { oauth2ClientInfos?: Array | Array; } From 0fb27016ec18cd512a18ff08eec69edfa4ce5712 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 17 Jan 2025 10:58:19 +0200 Subject: [PATCH 071/108] UI: Add unsubscribe valueChanges in rule node --- .../action/attributes-config.component.ts | 5 +- .../action/create-alarm-config.component.ts | 5 +- .../delete-attributes-config.component.ts | 5 +- .../common/alarm-status-select.component.ts | 20 +++----- .../common/arguments-map-config.component.ts | 36 +++++++-------- .../common/credentials-config.component.ts | 46 ++++++++----------- ...device-relations-query-config.component.ts | 12 +++-- .../common/kv-map-config-old.component.ts | 43 ++++++++--------- .../common/kv-map-config.component.ts | 19 +++----- .../math-function-autocomplete.component.ts | 4 +- .../common/message-types-config.component.ts | 2 +- .../common/msg-metadata-chip.component.ts | 20 +++----- ...put-message-type-autocomplete.component.ts | 18 +++----- .../relations-query-config-old.component.ts | 17 +++---- .../relations-query-config.component.ts | 17 +++---- .../common/select-attributes.component.ts | 20 +++----- .../common/sv-map-config.component.ts | 42 ++++++----------- .../src/app/shared/models/rule-node.models.ts | 2 +- 18 files changed, 145 insertions(+), 188 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/attributes-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/attributes-config.component.ts index a60a4ae40e..29b5dd2bc5 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/attributes-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/attributes-config.component.ts @@ -18,6 +18,7 @@ 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', @@ -48,7 +49,9 @@ export class AttributesConfigComponent extends RuleNodeConfigurationComponent { updateAttributesOnlyOnValueChange: [configuration ? configuration.updateAttributesOnlyOnValueChange : false, []] }); - this.attributesConfigForm.get('scope').valueChanges.subscribe((value) => { + this.attributesConfigForm.get('scope').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((value) => { if (value !== AttributeScope.SHARED_SCOPE) { this.attributesConfigForm.get('notifyDevice').patchValue(false, {emitEvent: false}); } diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/create-alarm-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/create-alarm-config.component.ts index a68eedfbfc..738ec589be 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/create-alarm-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/create-alarm-config.component.ts @@ -29,6 +29,7 @@ import { 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', @@ -83,7 +84,9 @@ export class CreateAlarmConfigComponent extends RuleNodeConfigurationComponent { dynamicSeverity: false }); - this.createAlarmConfigForm.get('dynamicSeverity').valueChanges.subscribe((dynamicSeverity) => { + this.createAlarmConfigForm.get('dynamicSeverity').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((dynamicSeverity) => { if(dynamicSeverity){ this.createAlarmConfigForm.get('severity').patchValue('',{emitEvent:false}); } else { diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/delete-attributes-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/delete-attributes-config.component.ts index 758e0ecb48..b61f9aa7c1 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/delete-attributes-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/delete-attributes-config.component.ts @@ -20,6 +20,7 @@ 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', @@ -51,7 +52,9 @@ export class DeleteAttributesConfigComponent extends RuleNodeConfigurationCompon notifyDevice: [configuration ? configuration.notifyDevice : false, []] }); - this.deleteAttributesConfigForm.get('scope').valueChanges.subscribe((value) => { + this.deleteAttributesConfigForm.get('scope').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((value) => { if (value !== AttributeScope.SHARED_SCOPE) { this.deleteAttributesConfigForm.get('notifyDevice').patchValue(false, {emitEvent: false}); } diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/alarm-status-select.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/alarm-status-select.component.ts index 923a4f13fc..f92f09b428 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/alarm-status-select.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/alarm-status-select.component.ts @@ -14,11 +14,10 @@ /// limitations under the License. /// -import { Component, forwardRef, OnDestroy, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, OnInit } from '@angular/core'; import { AlarmStatus, alarmStatusTranslations, PageComponent } from '@shared/public-api'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { takeUntil } from 'rxjs/operators'; -import { Subject } from 'rxjs'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-alarm-status-select', @@ -31,17 +30,17 @@ import { Subject } from 'rxjs'; }] }) -export class AlarmStatusSelectComponent extends PageComponent implements OnInit, ControlValueAccessor, OnDestroy { +export class AlarmStatusSelectComponent extends PageComponent implements OnInit, ControlValueAccessor { public alarmStatusGroup: FormGroup; private propagateChange = null; - private destroy$ = new Subject(); readonly alarmStatus = AlarmStatus; readonly alarmStatusTranslations = alarmStatusTranslations; - constructor(private fb: FormBuilder) { + constructor(private fb: FormBuilder, + private destroyRef: DestroyRef) { super(); } @@ -51,7 +50,7 @@ export class AlarmStatusSelectComponent extends PageComponent implements OnInit, }); this.alarmStatusGroup.get('alarmStatus').valueChanges.pipe( - takeUntil(this.destroy$) + takeUntilDestroyed(this.destroyRef) ).subscribe((value) => { this.propagateChange(value); }); @@ -69,12 +68,7 @@ export class AlarmStatusSelectComponent extends PageComponent implements OnInit, this.propagateChange = fn; } - registerOnTouched(fn: any): void { - } - - ngOnDestroy() { - this.destroy$.next(); - this.destroy$.complete(); + registerOnTouched(_fn: any): void { } writeValue(value: Array): void { diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/arguments-map-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/arguments-map-config.component.ts index 3b2e99cf96..3e6d5826f8 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/arguments-map-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/arguments-map-config.component.ts @@ -14,12 +14,11 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, FormArray, FormBuilder, - FormControl, FormGroup, NG_VALIDATORS, NG_VALUE_ACCESSOR, @@ -27,7 +26,6 @@ import { Validators } from '@angular/forms'; import { PageComponent } from '@shared/public-api'; -import { Subscription } from 'rxjs'; import { CdkDragDrop } from '@angular/cdk/drag-drop'; import { ArgumentName, @@ -38,6 +36,7 @@ import { MathFunction, MathFunctionMap } from './../rule-node-config.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-arguments-map-config', @@ -56,7 +55,7 @@ import { } ] }) -export class ArgumentsMapConfigComponent extends PageComponent implements ControlValueAccessor, OnInit, OnDestroy, Validator { +export class ArgumentsMapConfigComponent extends PageComponent implements ControlValueAccessor, OnInit, Validator { @Input() disabled: boolean; @@ -90,9 +89,8 @@ export class ArgumentsMapConfigComponent extends PageComponent implements Contro private propagateChange = null; - private valueChangeSubscription: Subscription[] = []; - - constructor(private fb: FormBuilder) { + constructor(private fb: FormBuilder, + private destroyRef: DestroyRef) { super(); } @@ -101,9 +99,11 @@ export class ArgumentsMapConfigComponent extends PageComponent implements Contro arguments: this.fb.array([]) }); - this.valueChangeSubscription.push(this.argumentsFormGroup.valueChanges.subscribe(() => { + this.argumentsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateModel(); - })); + }); this.setupArgumentsFormGroup(); } @@ -124,7 +124,7 @@ export class ArgumentsMapConfigComponent extends PageComponent implements Contro this.propagateChange = fn; } - registerOnTouched(fn: any): void { + registerOnTouched(_fn: any): void { } setDisabledState(isDisabled: boolean): void { @@ -138,13 +138,7 @@ export class ArgumentsMapConfigComponent extends PageComponent implements Contro } } - ngOnDestroy() { - if (this.valueChangeSubscription.length) { - this.valueChangeSubscription.forEach(sub => sub.unsubscribe()); - } - } - - writeValue(argumentsList): void { + writeValue(argumentsList: Array): void { const argumentsControls: Array = []; if (argumentsList) { argumentsList.forEach((property, index) => { @@ -167,7 +161,7 @@ export class ArgumentsMapConfigComponent extends PageComponent implements Contro argumentsFormArray.push(argumentControl, {emitEvent}); } - public validate(c: FormControl) { + public validate() { if (!this.argumentsFormGroup.valid) { return { argumentsRequired: true @@ -203,11 +197,13 @@ export class ArgumentsMapConfigComponent extends PageComponent implements Contro defaultValue: [property?.defaultValue ? property?.defaultValue : null] }); this.updateArgumentControlValidators(argumentControl); - this.valueChangeSubscription.push(argumentControl.get('type').valueChanges.subscribe(() => { + argumentControl.get('type').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { this.updateArgumentControlValidators(argumentControl); argumentControl.get('attributeScope').updateValueAndValidity({emitEvent: false}); argumentControl.get('defaultValue').updateValueAndValidity({emitEvent: false}); - })); + }); return argumentControl; } diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/credentials-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/credentials-config.component.ts index 97f0ba0846..95ed59683d 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/credentials-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/credentials-config.component.ts @@ -14,11 +14,10 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; import { ControlValueAccessor, FormBuilder, - FormControl, FormGroup, NG_VALIDATORS, NG_VALUE_ACCESSOR, @@ -27,12 +26,11 @@ import { ValidatorFn, Validators } from '@angular/forms'; -import { AppState, isDefinedAndNotNull } from '@core/public-api'; +import { isDefinedAndNotNull } from '@core/public-api'; import { PageComponent } from '@shared/public-api'; -import { Store } from '@ngrx/store'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { credentialsType, credentialsTypes, credentialsTypeTranslations } from '../rule-node-config.models'; -import { Subscription } from 'rxjs'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; interface CredentialsConfig { type: credentialsType; @@ -63,12 +61,10 @@ interface CredentialsConfig { } ] }) -export class CredentialsConfigComponent extends PageComponent implements ControlValueAccessor, OnInit, Validator, OnDestroy, OnChanges { +export class CredentialsConfigComponent extends PageComponent implements ControlValueAccessor, OnInit, Validator, OnChanges { credentialsConfigFormGroup: FormGroup; - subscriptions: Subscription[] = []; - private requiredValue: boolean; get required(): boolean { @@ -91,9 +87,9 @@ export class CredentialsConfigComponent extends PageComponent implements Control private propagateChange = (_: any) => {}; - constructor(protected store: Store, - private fb: FormBuilder) { - super(store); + constructor(private fb: FormBuilder, + private destroyRef: DestroyRef) { + super(); } ngOnInit(): void { @@ -110,16 +106,16 @@ export class CredentialsConfigComponent extends PageComponent implements Control certFileName: [null, []] } ); - this.subscriptions.push( - this.credentialsConfigFormGroup.valueChanges.subscribe(() => { - this.updateView(); - }) - ); - this.subscriptions.push( - this.credentialsConfigFormGroup.get('type').valueChanges.subscribe(() => { - this.credentialsTypeChanged(); - }) - ); + this.credentialsConfigFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.updateView(); + }); + this.credentialsConfigFormGroup.get('type').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.credentialsTypeChanged(); + }); } ngOnChanges(changes: SimpleChanges): void { @@ -138,10 +134,6 @@ export class CredentialsConfigComponent extends PageComponent implements Control } } - ngOnDestroy() { - this.subscriptions.forEach(s => s.unsubscribe()); - } - writeValue(credentials: CredentialsConfig | null): void { if (isDefinedAndNotNull(credentials)) { this.credentialsConfigFormGroup.reset(credentials, {emitEvent: false}); @@ -185,10 +177,10 @@ export class CredentialsConfigComponent extends PageComponent implements Control this.propagateChange = fn; } - registerOnTouched(fn: any): void { + registerOnTouched(): void { } - public validate(c: FormControl) { + public validate() { return this.credentialsConfigFormGroup.valid ? null : { credentialsConfig: { valid: false, diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/device-relations-query-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/device-relations-query-config.component.ts index 23aee0b29a..bc9f524a75 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/device-relations-query-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/device-relations-query-config.component.ts @@ -14,12 +14,13 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { PageComponent } from '@shared/components/page.component'; import { EntitySearchDirection, entitySearchDirectionTranslations } from '@app/shared/models/relation.models'; import { EntityType } from '@shared/models/entity-type.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; interface DeviceRelationsQuery { fetchLastLevelOnly: boolean; @@ -65,7 +66,8 @@ export class DeviceRelationsQueryConfigComponent extends PageComponent implement private propagateChange = null; - constructor(private fb: FormBuilder) { + constructor(private fb: FormBuilder, + private destroyRef: DestroyRef) { super(); } @@ -77,7 +79,9 @@ export class DeviceRelationsQueryConfigComponent extends PageComponent implement relationType: [null], deviceTypes: [null, [Validators.required]] }); - this.deviceRelationsQueryFormGroup.valueChanges.subscribe((query: DeviceRelationsQuery) => { + this.deviceRelationsQueryFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((query: DeviceRelationsQuery) => { if (this.deviceRelationsQueryFormGroup.valid) { this.propagateChange(query); } else { @@ -90,7 +94,7 @@ export class DeviceRelationsQueryConfigComponent extends PageComponent implement this.propagateChange = fn; } - registerOnTouched(fn: any): void { + registerOnTouched(_fn: any): void { } setDisabledState(isDisabled: boolean): void { diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config-old.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config-old.component.ts index a08394479a..1f7407c5e3 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config-old.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config-old.component.ts @@ -14,13 +14,12 @@ /// limitations under the License. /// -import { Component, forwardRef, Injector, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Injector, Input, OnInit } from '@angular/core'; import { AbstractControl, ControlValueAccessor, FormArray, FormBuilder, - FormControl, FormGroup, NG_VALIDATORS, NG_VALUE_ACCESSOR, @@ -29,11 +28,9 @@ import { Validators } from '@angular/forms'; import { PageComponent } from '@shared/public-api'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/public-api'; -import { Subscription } from 'rxjs'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { TranslateService } from '@ngx-translate/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-kv-map-config-old', @@ -87,13 +84,11 @@ export class KvMapConfigOldComponent extends PageComponent implements ControlVal private propagateChange = null; - private valueChangeSubscription: Subscription = null; - - constructor(protected store: Store, - public translate: TranslateService, - public injector: Injector, - private fb: FormBuilder) { - super(store); + constructor(public translate: TranslateService, + private injector: Injector, + private fb: FormBuilder, + private destroyRef: DestroyRef) { + super(); } ngOnInit(): void { @@ -101,9 +96,15 @@ export class KvMapConfigOldComponent extends PageComponent implements ControlVal if (this.ngControl != null) { this.ngControl.valueAccessor = this; } - this.kvListFormGroup = this.fb.group({}); - this.kvListFormGroup.addControl('keyVals', - this.fb.array([])); + this.kvListFormGroup = this.fb.group({ + keyVals: this.fb.array([]) + }); + + this.kvListFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.updateModel(); + }); } keyValsFormArray(): FormArray { @@ -114,7 +115,7 @@ export class KvMapConfigOldComponent extends PageComponent implements ControlVal this.propagateChange = fn; } - registerOnTouched(fn: any): void { + registerOnTouched(_fn: any): void { } setDisabledState(isDisabled: boolean): void { @@ -127,9 +128,6 @@ export class KvMapConfigOldComponent extends PageComponent implements ControlVal } writeValue(keyValMap: { [key: string]: string }): void { - if (this.valueChangeSubscription) { - this.valueChangeSubscription.unsubscribe(); - } const keyValsControls: Array = []; if (keyValMap) { for (const property of Object.keys(keyValMap)) { @@ -141,10 +139,7 @@ export class KvMapConfigOldComponent extends PageComponent implements ControlVal } } } - this.kvListFormGroup.setControl('keyVals', this.fb.array(keyValsControls)); - this.valueChangeSubscription = this.kvListFormGroup.valueChanges.subscribe(() => { - this.updateModel(); - }); + this.kvListFormGroup.setControl('keyVals', this.fb.array(keyValsControls), {emitEvent: false}); } public removeKeyVal(index: number) { @@ -159,7 +154,7 @@ export class KvMapConfigOldComponent extends PageComponent implements ControlVal })); } - public validate(c: FormControl) { + public validate() { const kvList: { key: string; value: string }[] = this.kvListFormGroup.get('keyVals').value; if (!kvList.length && this.required) { return { diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config.component.ts index 042dcf94bd..f46b5e9923 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/kv-map-config.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Injector, Input, OnDestroy, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Injector, Input, OnInit } from '@angular/core'; import { AbstractControl, ControlValueAccessor, @@ -31,7 +31,7 @@ import { } from '@angular/forms'; import { coerceBoolean } from '@shared/public-api'; import { isEqual } from '@core/public-api'; -import { Subject, takeUntil } from 'rxjs'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-kv-map-config', @@ -50,10 +50,9 @@ import { Subject, takeUntil } from 'rxjs'; } ] }) -export class KvMapConfigComponent implements ControlValueAccessor, OnInit, Validator, OnDestroy { +export class KvMapConfigComponent implements ControlValueAccessor, OnInit, Validator { private propagateChange: (value: any) => void = () => {}; - private destroy$ = new Subject(); kvListFormGroup: FormGroup; ngControl: NgControl; @@ -87,7 +86,8 @@ export class KvMapConfigComponent implements ControlValueAccessor, OnInit, Valid required = false; constructor(private injector: Injector, - private fb: FormBuilder) { + private fb: FormBuilder, + private destroyRef: DestroyRef) { } ngOnInit(): void { @@ -101,17 +101,12 @@ export class KvMapConfigComponent implements ControlValueAccessor, OnInit, Valid }, {validators: [this.propagateNestedErrors, this.oneMapRequiredValidator]}); this.kvListFormGroup.valueChanges - .pipe(takeUntil(this.destroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(() => { this.updateModel(); }); } - ngOnDestroy() { - this.destroy$.next(); - this.destroy$.complete(); - } - keyValsFormArray(): FormArray { return this.kvListFormGroup.get('keyVals') as FormArray; } @@ -120,7 +115,7 @@ export class KvMapConfigComponent implements ControlValueAccessor, OnInit, Valid this.propagateChange = fn; } - registerOnTouched(fn: any): void { + registerOnTouched(_fn: any): void { } setDisabledState(isDisabled: boolean): void { diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/math-function-autocomplete.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/math-function-autocomplete.component.ts index 1583048073..fb34cf4418 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/math-function-autocomplete.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/math-function-autocomplete.component.ts @@ -75,7 +75,7 @@ export class MathFunctionAutocompleteComponent implements ControlValueAccessor, }); this.filteredOptions = this.mathFunctionForm.get('operation').valueChanges.pipe( tap(value => { - let modelValue; + let modelValue: MathFunction; if (typeof value === 'string' && MathFunction[value]) { modelValue = MathFunction[value]; } else { @@ -101,7 +101,7 @@ export class MathFunctionAutocompleteComponent implements ControlValueAccessor, this.propagateChange = fn; } - registerOnTouched(fn: any): void { + registerOnTouched(_fn: any): void { } setDisabledState(isDisabled: boolean): void { diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/message-types-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/message-types-config.component.ts index 686c6150aa..c5d10a2d25 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/message-types-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/message-types-config.component.ts @@ -98,7 +98,7 @@ export class MessageTypesConfigComponent extends PageComponent implements Contro this.propagateChange = fn; } - registerOnTouched(fn: any): void { + registerOnTouched(_fn: any): void { } ngOnInit() { diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/msg-metadata-chip.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/msg-metadata-chip.component.ts index 066cea455e..a8e00a68e4 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/msg-metadata-chip.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/msg-metadata-chip.component.ts @@ -14,12 +14,11 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; import { FetchTo, FetchToTranslation } from '../rule-node-config.models'; -import { takeUntil } from 'rxjs/operators'; -import { Subject } from 'rxjs'; import { TranslateService } from '@ngx-translate/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-msg-metadata-chip', @@ -31,19 +30,19 @@ import { TranslateService } from '@ngx-translate/core'; }] }) -export class MsgMetadataChipComponent implements OnInit, ControlValueAccessor, OnDestroy { +export class MsgMetadataChipComponent implements OnInit, ControlValueAccessor { @Input() labelText: string; @Input() translation: Map = FetchToTranslation; private propagateChange: (value: any) => void = () => {}; - private destroy$ = new Subject(); public chipControlGroup: FormGroup; public selectOptions = []; constructor(private fb: FormBuilder, - private translate: TranslateService) {} + private translate: TranslateService, + private destroyRef: DestroyRef) {} ngOnInit(): void { this.initOptions(); @@ -52,7 +51,7 @@ export class MsgMetadataChipComponent implements OnInit, ControlValueAccessor, O }); this.chipControlGroup.get('chipControl').valueChanges.pipe( - takeUntil(this.destroy$) + takeUntilDestroyed(this.destroyRef) ).subscribe((value) => { if (value) { this.propagateChange(value); @@ -61,11 +60,6 @@ export class MsgMetadataChipComponent implements OnInit, ControlValueAccessor, O ); } - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - initOptions() { for (const key of this.translation.keys()) { this.selectOptions.push({ @@ -83,7 +77,7 @@ export class MsgMetadataChipComponent implements OnInit, ControlValueAccessor, O this.propagateChange = fn; } - registerOnTouched(fn: any): void { + registerOnTouched(_fn: any): void { } setDisabledState(isDisabled: boolean): void { diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/output-message-type-autocomplete.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/output-message-type-autocomplete.component.ts index 494b686ba3..b824240968 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/output-message-type-autocomplete.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/output-message-type-autocomplete.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnDestroy } from '@angular/core'; +import { Component, forwardRef, Input } from '@angular/core'; import { ControlValueAccessor, FormBuilder, @@ -26,7 +26,7 @@ import { } from '@angular/forms'; import { SubscriptSizing } from '@angular/material/form-field'; import { coerceBoolean } from '@shared/public-api'; -import { Subject, takeUntil } from 'rxjs'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; interface MessageType { name: string; @@ -51,7 +51,7 @@ interface MessageType { ] }) -export class OutputMessageTypeAutocompleteComponent implements ControlValueAccessor, Validator, OnDestroy { +export class OutputMessageTypeAutocompleteComponent implements ControlValueAccessor, Validator { @Input() subscriptSizing: SubscriptSizing = 'fixed'; @@ -93,7 +93,6 @@ export class OutputMessageTypeAutocompleteComponent implements ControlValueAcces private modelValue: string | null; private requiredValue: boolean; private propagateChange: (value: any) => void = () => {}; - private destroy$ = new Subject(); constructor(private fb: FormBuilder) { this.messageTypeFormGroup = this.fb.group({ @@ -101,19 +100,14 @@ export class OutputMessageTypeAutocompleteComponent implements ControlValueAcces messageType: [{value: null, disabled: true}, [Validators.maxLength(255)]] }); this.messageTypeFormGroup.get('messageTypeAlias').valueChanges - .pipe(takeUntil(this.destroy$)) + .pipe(takeUntilDestroyed()) .subscribe(value => this.updateMessageTypeValue(value)); this.messageTypeFormGroup.valueChanges - .pipe(takeUntil(this.destroy$)) + .pipe(takeUntilDestroyed()) .subscribe(() => this.updateView()); } - ngOnDestroy() { - this.destroy$.next(); - this.destroy$.complete(); - } - - registerOnTouched(fn: any): void { + registerOnTouched(_fn: any): void { } registerOnChange(fn: any): void { diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config-old.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config-old.component.ts index e89cd249f8..261b2aadc3 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config-old.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config-old.component.ts @@ -14,13 +14,12 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { EntitySearchDirection, entitySearchDirectionTranslations, PageComponent } from '@shared/public-api'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/public-api'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { RelationsQuery } from '../rule-node-config.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-relations-query-config-old', @@ -56,9 +55,9 @@ export class RelationsQueryConfigOldComponent extends PageComponent implements C private propagateChange = null; - constructor(protected store: Store, - private fb: FormBuilder) { - super(store); + constructor(private fb: FormBuilder, + private destroyRef: DestroyRef) { + super(); } ngOnInit(): void { @@ -68,7 +67,9 @@ export class RelationsQueryConfigOldComponent extends PageComponent implements C maxLevel: [null, []], filters: [null] }); - this.relationsQueryFormGroup.valueChanges.subscribe((query: RelationsQuery) => { + this.relationsQueryFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((query: RelationsQuery) => { if (this.relationsQueryFormGroup.valid) { this.propagateChange(query); } else { @@ -81,7 +82,7 @@ export class RelationsQueryConfigOldComponent extends PageComponent implements C this.propagateChange = fn; } - registerOnTouched(fn: any): void { + registerOnTouched(_fn: any): void { } setDisabledState(isDisabled: boolean): void { diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config.component.ts index d17e6f891f..5ed0eadbcc 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/relations-query-config.component.ts @@ -14,13 +14,12 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { EntitySearchDirection, entitySearchDirectionTranslations, PageComponent } from '@shared/public-api'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/public-api'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { RelationsQuery } from '../rule-node-config.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-relations-query-config', @@ -55,9 +54,9 @@ export class RelationsQueryConfigComponent extends PageComponent implements Cont private propagateChange = null; - constructor(protected store: Store, - private fb: FormBuilder) { - super(store); + constructor(private fb: FormBuilder, + private destroyRef: DestroyRef) { + super(); } ngOnInit(): void { @@ -67,7 +66,9 @@ export class RelationsQueryConfigComponent extends PageComponent implements Cont maxLevel: [null, [Validators.min(1)]], filters: [null] }); - this.relationsQueryFormGroup.valueChanges.subscribe((query: RelationsQuery) => { + this.relationsQueryFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((query: RelationsQuery) => { if (this.relationsQueryFormGroup.valid) { this.propagateChange(query); } else { @@ -80,7 +81,7 @@ export class RelationsQueryConfigComponent extends PageComponent implements Cont this.propagateChange = fn; } - registerOnTouched(fn: any): void { + registerOnTouched(_fn: any): void { } setDisabledState(isDisabled: boolean): void { diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/select-attributes.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/select-attributes.component.ts index fbfbb63a14..2bebd875ae 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/select-attributes.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/select-attributes.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, FormBuilder, @@ -25,11 +25,10 @@ import { ValidatorFn, Validators } from '@angular/forms'; -import { takeUntil } from 'rxjs/operators'; -import { Subject } from 'rxjs'; import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; import { TranslateService } from '@ngx-translate/core'; import { isDefinedAndNotNull } from '@core/public-api'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-select-attributes', @@ -46,10 +45,9 @@ import { isDefinedAndNotNull } from '@core/public-api'; }] }) -export class SelectAttributesComponent implements OnInit, ControlValueAccessor, OnDestroy { +export class SelectAttributesComponent implements OnInit, ControlValueAccessor { private propagateChange = (v: any) => { }; - private destroy$ = new Subject(); public attributeControlGroup: FormGroup; public separatorKeysCodes = [ENTER, COMMA, SEMICOLON]; @@ -58,7 +56,8 @@ export class SelectAttributesComponent implements OnInit, ControlValueAccessor, @Input() popupHelpLink: string; constructor(public translate: TranslateService, - private fb: FormBuilder) { + private fb: FormBuilder, + private destroyRef: DestroyRef) { } ngOnInit(): void { @@ -74,7 +73,7 @@ export class SelectAttributesComponent implements OnInit, ControlValueAccessor, }); this.attributeControlGroup.valueChanges.pipe( - takeUntil(this.destroy$) + takeUntilDestroyed(this.destroyRef) ).subscribe((value) => { this.propagateChange(this.preparePropagateValue(value)); }); @@ -88,7 +87,7 @@ export class SelectAttributesComponent implements OnInit, ControlValueAccessor, } else { formatValue[key] = isDefinedAndNotNull(propagateValue[key]) ? propagateValue[key] : []; } - }; + } return formatValue; }; @@ -131,9 +130,4 @@ export class SelectAttributesComponent implements OnInit, ControlValueAccessor, this.attributeControlGroup.enable({emitEvent: false}); } } - - ngOnDestroy(): void { - this.destroy$.next(null); - this.destroy$.complete(); - } } diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/sv-map-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/sv-map-config.component.ts index df9d3f0b3d..0a836fc508 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/sv-map-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/sv-map-config.component.ts @@ -14,13 +14,12 @@ /// limitations under the License. /// -import { Component, forwardRef, Injector, Input, OnDestroy, OnInit } from '@angular/core'; +import { Component, DestroyRef, forwardRef, Injector, Input, OnInit } from '@angular/core'; import { AbstractControl, ControlValueAccessor, FormArray, FormBuilder, - FormControl, FormGroup, NG_VALIDATORS, NG_VALUE_ACCESSOR, @@ -31,12 +30,10 @@ import { Validators } from '@angular/forms'; import { coerceBoolean, PageComponent } from '@shared/public-api'; -import { Store } from '@ngrx/store'; -import { AppState, isDefinedAndNotNull, isEqual } from '@core/public-api'; -import { Subject, Subscription } from 'rxjs'; +import { isDefinedAndNotNull, isEqual } from '@core/public-api'; import { TranslateService } from '@ngx-translate/core'; -import { takeUntil } from 'rxjs/operators'; import { OriginatorFieldsMappingValues, SvMapOption } from '../rule-node-config.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-sv-map-config', @@ -55,10 +52,8 @@ import { OriginatorFieldsMappingValues, SvMapOption } from '../rule-node-config. } ] }) -export class SvMapConfigComponent extends PageComponent implements ControlValueAccessor, OnInit, Validator, OnDestroy { +export class SvMapConfigComponent extends PageComponent implements ControlValueAccessor, OnInit, Validator { - private destroy$ = new Subject(); - private sourceFieldSubcritption: Subscription[] = []; private propagateChange = null; svListFormGroup: FormGroup; @@ -92,11 +87,11 @@ export class SvMapConfigComponent extends PageComponent implements ControlValueA @coerceBoolean() required = false; - constructor(protected store: Store, - public translate: TranslateService, - public injector: Injector, - private fb: FormBuilder) { - super(store); + constructor(public translate: TranslateService, + private injector: Injector, + private fb: FormBuilder, + private destroyRef: DestroyRef) { + super(); } ngOnInit(): void { @@ -110,17 +105,12 @@ export class SvMapConfigComponent extends PageComponent implements ControlValueA }, {validators: [this.propagateNestedErrors, this.oneMapRequiredValidator]}); this.svListFormGroup.valueChanges - .pipe(takeUntil(this.destroy$)) + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(() => { this.updateModel(); }); } - ngOnDestroy() { - this.destroy$.next(); - this.destroy$.complete(); - } - keyValsFormArray(): FormArray { return this.svListFormGroup.get('keyVals') as FormArray; } @@ -129,7 +119,7 @@ export class SvMapConfigComponent extends PageComponent implements ControlValueA this.propagateChange = fn; } - registerOnTouched(fn: any): void { + registerOnTouched(_fn: any): void { } setDisabledState(isDisabled: boolean): void { @@ -215,8 +205,6 @@ export class SvMapConfigComponent extends PageComponent implements ControlValueA public removeKeyVal(index: number) { this.keyValsFormArray().removeAt(index); - this.sourceFieldSubcritption[index].unsubscribe(); - this.sourceFieldSubcritption.splice(index, 1); } public addKeyVal() { @@ -228,15 +216,15 @@ export class SvMapConfigComponent extends PageComponent implements ControlValueA } private keyChangeSubscribe(formGroup: FormGroup) { - this.sourceFieldSubcritption.push(formGroup.get('key').valueChanges.pipe( - takeUntil(this.destroy$) + formGroup.get('key').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) ).subscribe((value) => { const mappedValue = OriginatorFieldsMappingValues.get(value); formGroup.get('value').patchValue(this.targetKeyPrefix + mappedValue[0].toUpperCase() + mappedValue.slice(1)); - })); + }); } - public validate(c: FormControl) { + public validate() { const svList: { key: string; value: string }[] = this.svListFormGroup.get('keyVals').value; if (!svList.length && this.required) { return { diff --git a/ui-ngx/src/app/shared/models/rule-node.models.ts b/ui-ngx/src/app/shared/models/rule-node.models.ts index 86e304b3df..471594f689 100644 --- a/ui-ngx/src/app/shared/models/rule-node.models.ts +++ b/ui-ngx/src/app/shared/models/rule-node.models.ts @@ -101,7 +101,7 @@ export abstract class RuleNodeConfigurationComponent extends PageComponent imple private configurationSet = false; private disabledValue = false; - private destroyRef = inject(DestroyRef); + protected destroyRef = inject(DestroyRef); set disabled(value: boolean) { if (this.disabledValue !== value) { From 0b277661bdba594b2d22b04914a30cde039b1c23 Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Fri, 17 Jan 2025 12:36:52 +0200 Subject: [PATCH 072/108] Save time series strategies: add tests for changes in save time series node --- .../engine/telemetry/TbMsgTimeseriesNode.java | 6 +- .../TbMsgTimeseriesNodeConfiguration.java | 11 +- .../DeduplicatePersistenceStrategy.java | 5 + .../telemetry/TbMsgTimeseriesNodeTest.java | 365 ++++++++++++++++-- 4 files changed, 342 insertions(+), 45 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java index db2421e307..422065f81f 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java @@ -90,11 +90,11 @@ public class TbMsgTimeseriesNode implements TbNode { onTenantProfileUpdate(ctx.getTenantProfile()); persistenceSettings = config.getPersistenceSettings(); if (persistenceSettings == null) { - throw new TbNodeException("Persistence settings cannot be null!", true); + throw new TbNodeException("Persistence settings cannot be null", true); } } - void onTenantProfileUpdate(TenantProfile tenantProfile) { + private void onTenantProfileUpdate(TenantProfile tenantProfile) { DefaultTenantProfileConfiguration configuration = (DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration(); tenantProfileDefaultStorageTtl = TimeUnit.DAYS.toSeconds(configuration.getDefaultStorageTtlDays()); } @@ -193,7 +193,7 @@ public class TbMsgTimeseriesNode implements TbNode { boolean hasChanges = false; switch (fromVersion) { case 0: - if (oldConfiguration.has("persistenceSettings")) { + if (oldConfiguration.has("persistenceSettings") && !oldConfiguration.has("skipLatestPersistence")) { break; } hasChanges = true; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java index 8be8cac24e..f702ecc6da 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java @@ -16,6 +16,7 @@ package org.thingsboard.rule.engine.telemetry; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; @@ -67,11 +68,15 @@ public class TbMsgTimeseriesNodeConfiguration implements NodeConfiguration Sets.newConcurrentHashSet()); } + @JsonProperty("deduplicationIntervalSecs") + public long getDeduplicationIntervalSecs() { + return Duration.ofMillis(deduplicationIntervalMillis).toSeconds(); + } + @Override public boolean shouldPersist(long ts, UUID originatorUuid) { long intervalNumber = ts / deduplicationIntervalMillis; diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java index 71d060aab2..d0727b1a3c 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java @@ -41,6 +41,7 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.DoubleDataEntry; import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.msg.TbMsgType; @@ -57,25 +58,30 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.assertArg; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { private final TenantId TENANT_ID = TenantId.fromUUID(UUID.fromString("c8f34868-603a-4433-876a-7d356e5cf377")); private final DeviceId DEVICE_ID = new DeviceId(UUID.fromString("e5095e9a-04f4-44c9-b443-1cf1b97d3384")); - private final TenantProfileId TENANT_PROFILE_ID = new TenantProfileId(UUID.fromString("ab78dd78-83d0-43fa-869f-d42ec9ed1744")); + + private TenantProfile tenantProfile; private TbMsgTimeseriesNode node; private TbMsgTimeseriesNodeConfiguration config; - private long tenantProfileDefaultStorageTtl; @Mock private TbContext ctxMock; @@ -84,6 +90,17 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { @BeforeEach public void setUp() throws TbNodeException { + tenantProfile = new TenantProfile(new TenantProfileId(UUID.fromString("ab78dd78-83d0-43fa-869f-d42ec9ed1744"))); + var tenantProfileConfiguration = new DefaultTenantProfileConfiguration(); + tenantProfileConfiguration.setDefaultStorageTtlDays(5); + var tenantProfileData = new TenantProfileData(); + tenantProfileData.setConfiguration(tenantProfileConfiguration); + tenantProfile.setProfileData(tenantProfileData); + lenient().when(ctxMock.getTenantProfile()).thenReturn(tenantProfile); + + lenient().when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + lenient().when(ctxMock.getTelemetryService()).thenReturn(telemetryServiceMock); + node = spy(new TbMsgTimeseriesNode()); config = new TbMsgTimeseriesNodeConfiguration().defaultConfiguration(); } @@ -95,18 +112,47 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { assertThat(config.isUseServerTs()).isFalse(); } + @Test + public void whenInit_thenShouldAddTenantProfileListener() throws Exception { + // GIVEN-WHEN + node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + // THEN + then(ctxMock).should().addTenantProfileListener(any()); + } + + @Test + public void givenPersistenceSettingsAreNull_whenInit_thenThrowsException() { + // GIVEN + config.setPersistenceSettings(null); + + // WHEN-THEN + assertThatThrownBy(() -> node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config)))) + .isInstanceOf(TbNodeException.class) + .matches(e -> ((TbNodeException) e).isUnrecoverable()) + .hasMessage("Persistence settings cannot be null"); + } + @ParameterizedTest @EnumSource(TbMsgType.class) public void givenMsgTypeAndEmptyMsgData_whenOnMsg_thenVerifyFailureMsg(TbMsgType msgType) throws TbNodeException { - init(); + // GIVEN + node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + TbMsg msg = TbMsg.newMsg() .type(msgType) .originator(DEVICE_ID) .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_ARRAY) .build(); + + // WHEN node.onMsg(ctxMock, msg); + // THEN + then(ctxMock).should().addTenantProfileListener(any()); + then(ctxMock).should().getTenantProfile(); + ArgumentCaptor throwableCaptor = ArgumentCaptor.forClass(Throwable.class); verify(ctxMock).tellFailure(eq(msg), throwableCaptor.capture()); @@ -120,9 +166,11 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { } @Test - public void givenTtlFromConfigIsZeroAndUseServiceTsIsTrue_whenOnMsg_thenSaveTimeseriesUsingTenantProfileDefaultTtl() throws TbNodeException { + public void givenTtlFromConfigIsZeroAndUseServerTsIsTrue_whenOnMsg_thenSaveTimeseriesUsingTenantProfileDefaultTtl() throws TbNodeException { + // GIVEN config.setUseServerTs(true); - init(); + + node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); String data = """ { @@ -137,23 +185,28 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { .data(data) .build(); - when(ctxMock.getTelemetryService()).thenReturn(telemetryServiceMock); - when(ctxMock.getTenantId()).thenReturn(TENANT_ID); doAnswer(invocation -> { TimeseriesSaveRequest request = invocation.getArgument(0); request.getCallback().onSuccess(null); return null; }).when(telemetryServiceMock).saveTimeseries(any(TimeseriesSaveRequest.class)); + // WHEN node.onMsg(ctxMock, msg); + // THEN + then(ctxMock).should().getTenantId(); + then(ctxMock).should().getTelemetryService(); + then(ctxMock).should().addTenantProfileListener(any()); + then(ctxMock).should().getTenantProfile(); + List expectedList = getTsKvEntriesListWithTs(data, System.currentTimeMillis()); verify(telemetryServiceMock).saveTimeseries(assertArg(request -> { assertThat(request.getTenantId()).isEqualTo(TENANT_ID); assertThat(request.getCustomerId()).isNull(); assertThat(request.getEntityId()).isEqualTo(DEVICE_ID); assertThat(request.getEntries()).usingRecursiveFieldByFieldElementComparatorIgnoringFields("ts").containsExactlyElementsOf(expectedList); - assertThat(request.getTtl()).isEqualTo(tenantProfileDefaultStorageTtl); + assertThat(request.getTtl()).isEqualTo(extractTtlAsSeconds(tenantProfile)); assertThat(request.isSaveLatest()).isTrue(); assertThat(request.getCallback()).isInstanceOf(TelemetryNodeCallback.class); })); @@ -162,9 +215,9 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { } @Test - public void givenSkipLatestPersistenceIsTrueAndTtlFromConfig_whenOnMsg_thenSaveTimeseriesUsingTtlFromConfig() throws TbNodeException { - long ttlFromConfig = 5L; - config.setDefaultTTL(ttlFromConfig); + public void givenSkipLatestPersistenceSettingsAndTtlFromConfig_whenOnMsg_thenSaveTimeseriesUsingTtlFromConfig() throws TbNodeException { + // GIVEN + config.setDefaultTTL(10L); var timeseriesStrategy = PersistenceStrategy.onEveryMessage(); var latestStrategy = PersistenceStrategy.skip(); @@ -172,7 +225,7 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { var persistenceSettings = new TbMsgTimeseriesNodeConfiguration.PersistenceSettings.Advanced(timeseriesStrategy, latestStrategy, webSockets); config.setPersistenceSettings(persistenceSettings); - init(); + node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); String data = """ { @@ -189,23 +242,28 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { .data(data) .build(); - when(ctxMock.getTelemetryService()).thenReturn(telemetryServiceMock); - when(ctxMock.getTenantId()).thenReturn(TENANT_ID); doAnswer(invocation -> { TimeseriesSaveRequest request = invocation.getArgument(0); request.getCallback().onSuccess(null); return null; }).when(telemetryServiceMock).saveTimeseries(any(TimeseriesSaveRequest.class)); + // WHEN node.onMsg(ctxMock, msg); + // THEN + then(ctxMock).should().getTenantId(); + then(ctxMock).should().getTelemetryService(); + then(ctxMock).should().addTenantProfileListener(any()); + then(ctxMock).should().getTenantProfile(); + List expectedList = getTsKvEntriesListWithTs(data, ts); verify(telemetryServiceMock).saveTimeseries(assertArg(request -> { assertThat(request.getTenantId()).isEqualTo(TENANT_ID); assertThat(request.getCustomerId()).isNull(); assertThat(request.getEntityId()).isEqualTo(DEVICE_ID); assertThat(request.getEntries()).containsExactlyElementsOf(expectedList); - assertThat(request.getTtl()).isEqualTo(ttlFromConfig); + assertThat(request.getTtl()).isEqualTo(config.getDefaultTTL()); assertThat(request.isSaveTimeseries()).isTrue(); assertThat(request.isSaveLatest()).isFalse(); assertThat(request.isSendWsUpdate()).isTrue(); @@ -218,11 +276,10 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { @ParameterizedTest @MethodSource public void givenTtlFromConfigAndTtlFromMd_whenOnMsg_thenVerifyTtl(String ttlFromMd, long ttlFromConfig, long expectedTtl) throws TbNodeException { + // GIVEN config.setDefaultTTL(ttlFromConfig); - init(); - when(ctxMock.getTelemetryService()).thenReturn(telemetryServiceMock); - when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); String data = """ { @@ -238,8 +295,11 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { .copyMetaData(metadata) .data(data) .build(); + + // WHEN node.onMsg(ctxMock, msg); + // THEN verify(telemetryServiceMock).saveTimeseries(assertArg(request -> { assertThat(request.getTenantId()).isEqualTo(TENANT_ID); assertThat(request.getCustomerId()).isNull(); @@ -262,26 +322,6 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { ); } - private void init() throws TbNodeException { - var configuration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); - var tenantProfile = getTenantProfile(); - when(ctxMock.getTenantProfile()).thenReturn(tenantProfile); - tenantProfile.getProfileConfiguration().ifPresent(profileConfiguration -> - tenantProfileDefaultStorageTtl = TimeUnit.DAYS.toSeconds(profileConfiguration.getDefaultStorageTtlDays())); - node.init(ctxMock, configuration); - verify(ctxMock).addTenantProfileListener(any()); - } - - private TenantProfile getTenantProfile() { - var tenantProfile = new TenantProfile(TENANT_PROFILE_ID); - var tenantProfileData = new TenantProfileData(); - var tenantProfileConfiguration = new DefaultTenantProfileConfiguration(); - tenantProfileConfiguration.setDefaultStorageTtlDays(5); - tenantProfileData.setConfiguration(tenantProfileConfiguration); - tenantProfile.setProfileData(tenantProfileData); - return tenantProfile; - } - private static List getTsKvEntriesListWithTs(String data, long ts) { Map> tsKvMap = JsonConverter.convertToTelemetry(JsonParser.parseString(data), ts); List expectedList = new ArrayList<>(); @@ -293,6 +333,253 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { return expectedList; } + @Test + public void givenOnEveryMessagePersistenceSettingsAndSameMessageTwoTimes_whenOnMsg_thenPersistSameMessageTwoTimes() throws TbNodeException { + // GIVEN + config.setPersistenceSettings(new TbMsgTimeseriesNodeConfiguration.PersistenceSettings.OnEveryMessage()); + + node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + var msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .data(JacksonUtil.newObjectNode().put("temperature", 22.3).toString()) + .metaData(new TbMsgMetaData(Map.of("ts", "123"))) + .build(); + + // WHEN-THEN + var expectedSaveRequest = TimeseriesSaveRequest.builder() + .tenantId(TENANT_ID) + .customerId(msg.getCustomerId()) + .entityId(msg.getOriginator()) + .entry(new BasicTsKvEntry(123L, new DoubleDataEntry("temperature", 22.3))) + .ttl(extractTtlAsSeconds(tenantProfile)) + .saveTimeseries(true) + .saveLatest(true) + .sendWsUpdate(true) + .build(); + + node.onMsg(ctxMock, msg); + then(telemetryServiceMock).should(times(1)).saveTimeseries(assertArg( + actualSaveRequest -> assertThat(actualSaveRequest).usingRecursiveComparison().ignoringFields("callback").isEqualTo(expectedSaveRequest) + )); + + node.onMsg(ctxMock, msg); + then(telemetryServiceMock).should(times(2)).saveTimeseries(assertArg( + actualSaveRequest -> assertThat(actualSaveRequest).usingRecursiveComparison().ignoringFields("callback").isEqualTo(expectedSaveRequest) + )); + } + + @Test + public void givenDeduplicatePersistenceSettingsAndSameMessageTwoTimes_whenOnMsg_thenPersistThisMessageOnlyFirstTime() throws TbNodeException { + // GIVEN + config.setPersistenceSettings(new TbMsgTimeseriesNodeConfiguration.PersistenceSettings.Deduplicate(10)); + + node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + var msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .data(JacksonUtil.newObjectNode().put("temperature", 22.3).toString()) + .metaData(new TbMsgMetaData(Map.of("ts", "123"))) + .build(); + + // WHEN-THEN + var expectedSaveRequest = TimeseriesSaveRequest.builder() + .tenantId(TENANT_ID) + .customerId(msg.getCustomerId()) + .entityId(msg.getOriginator()) + .entry(new BasicTsKvEntry(123L, new DoubleDataEntry("temperature", 22.3))) + .ttl(extractTtlAsSeconds(tenantProfile)) + .saveTimeseries(true) + .saveLatest(true) + .sendWsUpdate(true) + .build(); + + node.onMsg(ctxMock, msg); + then(telemetryServiceMock).should().saveTimeseries(assertArg( + actualSaveRequest -> assertThat(actualSaveRequest).usingRecursiveComparison().ignoringFields("callback").isEqualTo(expectedSaveRequest) + )); + + clearInvocations(telemetryServiceMock, ctxMock); + + node.onMsg(ctxMock, msg); + then(telemetryServiceMock).should(never()).saveTimeseries(any()); + } + + @Test + public void givenWebsocketsOnlyPersistenceSettingsAndSameMessageTwoTimes_whenOnMsg_thenSendsOnlyWsUpdateTwoTimes() throws TbNodeException { + // GIVEN + config.setPersistenceSettings(new TbMsgTimeseriesNodeConfiguration.PersistenceSettings.WebSocketsOnly()); + + node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + var msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .data(JacksonUtil.newObjectNode().put("temperature", 22.3).toString()) + .metaData(new TbMsgMetaData(Map.of("ts", "123"))) + .build(); + + // WHEN-THEN + var expectedSaveRequest = TimeseriesSaveRequest.builder() + .tenantId(TENANT_ID) + .customerId(msg.getCustomerId()) + .entityId(msg.getOriginator()) + .entry(new BasicTsKvEntry(123L, new DoubleDataEntry("temperature", 22.3))) + .ttl(extractTtlAsSeconds(tenantProfile)) + .saveTimeseries(false) + .saveLatest(false) + .sendWsUpdate(true) + .build(); + + node.onMsg(ctxMock, msg); + then(telemetryServiceMock).should(times(1)).saveTimeseries(assertArg( + actualSaveRequest -> assertThat(actualSaveRequest).usingRecursiveComparison().ignoringFields("callback").isEqualTo(expectedSaveRequest) + )); + + node.onMsg(ctxMock, msg); + then(telemetryServiceMock).should(times(2)).saveTimeseries(assertArg( + actualSaveRequest -> assertThat(actualSaveRequest).usingRecursiveComparison().ignoringFields("callback").isEqualTo(expectedSaveRequest) + )); + } + + @Test + public void givenAdvancedPersistenceSettingsWithOnEveryMessageStrategiesForAllActionsAndSameMessageTwoTimes_whenOnMsg_thenPersistSameMessageTwoTimes() throws TbNodeException { + // GIVEN + config.setPersistenceSettings(new TbMsgTimeseriesNodeConfiguration.PersistenceSettings.Advanced( + PersistenceStrategy.onEveryMessage(), + PersistenceStrategy.onEveryMessage(), + PersistenceStrategy.onEveryMessage() + )); + + node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + var msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .data(JacksonUtil.newObjectNode().put("temperature", 22.3).toString()) + .metaData(new TbMsgMetaData(Map.of("ts", "123"))) + .build(); + + // WHEN-THEN + var expectedSaveRequest = TimeseriesSaveRequest.builder() + .tenantId(TENANT_ID) + .customerId(msg.getCustomerId()) + .entityId(msg.getOriginator()) + .entry(new BasicTsKvEntry(123L, new DoubleDataEntry("temperature", 22.3))) + .ttl(extractTtlAsSeconds(tenantProfile)) + .saveTimeseries(true) + .saveLatest(true) + .sendWsUpdate(true) + .build(); + + node.onMsg(ctxMock, msg); + then(telemetryServiceMock).should(times(1)).saveTimeseries(assertArg( + actualSaveRequest -> assertThat(actualSaveRequest).usingRecursiveComparison().ignoringFields("callback").isEqualTo(expectedSaveRequest) + )); + + node.onMsg(ctxMock, msg); + then(telemetryServiceMock).should(times(2)).saveTimeseries(assertArg( + actualSaveRequest -> assertThat(actualSaveRequest).usingRecursiveComparison().ignoringFields("callback").isEqualTo(expectedSaveRequest) + )); + } + + @Test + public void givenAdvancedPersistenceSettingsWithDifferentDeduplicateStrategyForEachAction_whenOnMsg_thenEvaluatesStrategiesForEachActionsIndependently() throws TbNodeException { + // GIVEN + config.setPersistenceSettings(new TbMsgTimeseriesNodeConfiguration.PersistenceSettings.Advanced( + PersistenceStrategy.deduplicate(1), + PersistenceStrategy.deduplicate(2), + PersistenceStrategy.deduplicate(3) + )); + + node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + long ts1 = 500L; + long ts2 = 1500L; + long ts3 = 2500L; + + // WHEN-THEN + node.onMsg(ctxMock, TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .data(JacksonUtil.newObjectNode().put("temperature", 22.3).toString()) + .metaData(new TbMsgMetaData(Map.of("ts", Long.toString(ts1)))) + .build()); + then(telemetryServiceMock).should().saveTimeseries(assertArg( + actualSaveRequest -> { + assertThat(actualSaveRequest.isSaveTimeseries()).isTrue(); + assertThat(actualSaveRequest.isSaveLatest()).isTrue(); + assertThat(actualSaveRequest.isSendWsUpdate()).isTrue(); + } + )); + + clearInvocations(telemetryServiceMock); + + node.onMsg(ctxMock, TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .data(JacksonUtil.newObjectNode().put("temperature", 22.3).toString()) + .metaData(new TbMsgMetaData(Map.of("ts", Long.toString(ts2)))) + .build()); + then(telemetryServiceMock).should().saveTimeseries(assertArg( + actualSaveRequest -> { + assertThat(actualSaveRequest.isSaveTimeseries()).isTrue(); + assertThat(actualSaveRequest.isSaveLatest()).isFalse(); + assertThat(actualSaveRequest.isSendWsUpdate()).isFalse(); + } + )); + + clearInvocations(telemetryServiceMock); + + node.onMsg(ctxMock, TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .data(JacksonUtil.newObjectNode().put("temperature", 22.3).toString()) + .metaData(new TbMsgMetaData(Map.of("ts", Long.toString(ts3)))) + .build()); + then(telemetryServiceMock).should().saveTimeseries(assertArg( + actualSaveRequest -> { + assertThat(actualSaveRequest.isSaveTimeseries()).isTrue(); + assertThat(actualSaveRequest.isSaveLatest()).isTrue(); + assertThat(actualSaveRequest.isSendWsUpdate()).isFalse(); + } + )); + } + + @Test + public void givenAdvancedPersistenceSettingsWithSkipStrategiesForAllActionsAndSameMessageTwoTimes_whenOnMsg_thenSkipsSameMessageTwoTimes() throws TbNodeException { + // GIVEN + config.setPersistenceSettings(new TbMsgTimeseriesNodeConfiguration.PersistenceSettings.Advanced( + PersistenceStrategy.skip(), + PersistenceStrategy.skip(), + PersistenceStrategy.skip() + )); + + node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + var msg = TbMsg.newMsg() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(DEVICE_ID) + .data(JacksonUtil.newObjectNode().put("temperature", 22.3).toString()) + .metaData(new TbMsgMetaData(Map.of("ts", "123"))) + .build(); + + // WHEN-THEN + node.onMsg(ctxMock, msg); + then(telemetryServiceMock).should(never()).saveTimeseries(any()); + then(ctxMock).should(times(1)).tellSuccess(msg); + + node.onMsg(ctxMock, msg); + then(telemetryServiceMock).should(never()).saveTimeseries(any()); + then(ctxMock).should(times(2)).tellSuccess(msg); + } + + private static long extractTtlAsSeconds(TenantProfile tenantProfile) { + return TimeUnit.DAYS.toSeconds(tenantProfile.getDefaultProfileConfiguration().getDefaultStorageTtlDays()); + } + @Override protected TbNode getTestNode() { return node; From c3b1dfd82579e33ed0bb3713cda6a02e8b3f20ea Mon Sep 17 00:00:00 2001 From: mpetrov Date: Fri, 17 Jan 2025 12:46:24 +0200 Subject: [PATCH 073/108] Fixed oathclient - client id and secret removal with slow internet --- .../home/pages/admin/oauth2/clients/client.component.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client.component.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client.component.ts index 70355c0a3e..08d7782a7f 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client.component.ts @@ -203,7 +203,9 @@ export class ClientComponent extends EntityComponent { this.changeMapperConfigType(this.entityForm, value); From 787aa4a3c727bc031e22757fca250c8f6c36089f Mon Sep 17 00:00:00 2001 From: mpetrov Date: Fri, 17 Jan 2025 13:43:31 +0200 Subject: [PATCH 074/108] Fixed unnecessary scroll in JS Library alias on error tooltip showing --- ui-ngx/src/app/shared/components/js-func-modules.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/shared/components/js-func-modules.component.html b/ui-ngx/src/app/shared/components/js-func-modules.component.html index 7aa70afd1d..8323509b2b 100644 --- a/ui-ngx/src/app/shared/components/js-func-modules.component.html +++ b/ui-ngx/src/app/shared/components/js-func-modules.component.html @@ -27,7 +27,7 @@
    - From 73cd704e0831946c6424594d568ed721fedd8c1f Mon Sep 17 00:00:00 2001 From: mpetrov Date: Fri, 17 Jan 2025 14:00:50 +0200 Subject: [PATCH 075/108] Removed flex-1 --- ui-ngx/src/app/shared/components/js-func-modules.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/shared/components/js-func-modules.component.html b/ui-ngx/src/app/shared/components/js-func-modules.component.html index 8323509b2b..b011838212 100644 --- a/ui-ngx/src/app/shared/components/js-func-modules.component.html +++ b/ui-ngx/src/app/shared/components/js-func-modules.component.html @@ -27,7 +27,7 @@
    - From 71d43f3af2f3fbb1ce1fccd3a2424e3d3f86bd0e Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Fri, 17 Jan 2025 15:24:06 +0200 Subject: [PATCH 076/108] Save time series strategies: update configurationVersion for save time series node in rule chain JSONs --- .../data/json/edge/rule_chains/edge_root_rule_chain.json | 1 + .../json/tenant/device_profile/rule_chain_template.json | 1 + .../main/data/json/tenant/rule_chains/root_rule_chain.json | 1 + monitoring/src/main/resources/root_rule_chain.json | 6 +++--- .../src/test/resources/MqttRuleNodeTestMetadata.json | 2 +- 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/application/src/main/data/json/edge/rule_chains/edge_root_rule_chain.json b/application/src/main/data/json/edge/rule_chains/edge_root_rule_chain.json index e7b4b93e98..e663ff779d 100644 --- a/application/src/main/data/json/edge/rule_chains/edge_root_rule_chain.json +++ b/application/src/main/data/json/edge/rule_chains/edge_root_rule_chain.json @@ -33,6 +33,7 @@ }, "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", "name": "Save Timeseries", + "configurationVersion": 1, "configuration": { "defaultTTL": 0, "useServerTs": false, diff --git a/application/src/main/data/json/tenant/device_profile/rule_chain_template.json b/application/src/main/data/json/tenant/device_profile/rule_chain_template.json index 37cc226a6e..325e01003f 100644 --- a/application/src/main/data/json/tenant/device_profile/rule_chain_template.json +++ b/application/src/main/data/json/tenant/device_profile/rule_chain_template.json @@ -19,6 +19,7 @@ }, "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", "name": "Save Timeseries", + "configurationVersion": 1, "configuration": { "defaultTTL": 0, "useServerTs": false, diff --git a/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json b/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json index 7893a64fd2..4dc202d740 100644 --- a/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json +++ b/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json @@ -18,6 +18,7 @@ }, "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", "name": "Save Timeseries", + "configurationVersion": 1, "configuration": { "defaultTTL": 0, "useServerTs": false, diff --git a/monitoring/src/main/resources/root_rule_chain.json b/monitoring/src/main/resources/root_rule_chain.json index a48fd6bf35..eda3e44e1b 100644 --- a/monitoring/src/main/resources/root_rule_chain.json +++ b/monitoring/src/main/resources/root_rule_chain.json @@ -21,7 +21,7 @@ "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", "name": "Save Timeseries", "singletonMode": false, - "configurationVersion": 0, + "configurationVersion": 1, "configuration": { "defaultTTL": 0, "useServerTs": false, @@ -277,7 +277,7 @@ "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", "name": "Save Timeseries", "singletonMode": false, - "configurationVersion": 0, + "configurationVersion": 1, "configuration": { "defaultTTL": 0, "useServerTs": false, @@ -315,7 +315,7 @@ "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", "name": "Save Timeseries with TTL", "singletonMode": false, - "configurationVersion": 0, + "configurationVersion": 1, "configuration": { "defaultTTL": 180, "useServerTs": false, diff --git a/msa/black-box-tests/src/test/resources/MqttRuleNodeTestMetadata.json b/msa/black-box-tests/src/test/resources/MqttRuleNodeTestMetadata.json index adcb618cbe..c495e590cd 100644 --- a/msa/black-box-tests/src/test/resources/MqttRuleNodeTestMetadata.json +++ b/msa/black-box-tests/src/test/resources/MqttRuleNodeTestMetadata.json @@ -36,7 +36,7 @@ "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", "name": "save timeseries", "singletonMode": false, - "configurationVersion": 0, + "configurationVersion": 1, "configuration": { "defaultTTL": 0, "useServerTs": false, From e4a216bb87a13edc3dd0eef997d30e4c91822487 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 20 Jan 2025 11:25:59 +0200 Subject: [PATCH 077/108] UI: Add generic type to extractComponentsFromModule --- ui-ngx/src/app/core/http/rule-chain.service.ts | 2 +- ui-ngx/src/app/core/services/resources.service.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/core/http/rule-chain.service.ts b/ui-ngx/src/app/core/http/rule-chain.service.ts index 88bc4286dc..c7b7c04842 100644 --- a/ui-ngx/src/app/core/http/rule-chain.service.ts +++ b/ui-ngx/src/app/core/http/rule-chain.service.ts @@ -185,7 +185,7 @@ export class RuleChainService { } public registerSystemRuleNodeConfigModule(module: any) { - this.systemRuleNodeConfigComponents = this.resourcesService.extractComponentsFromModule(module, true); + this.systemRuleNodeConfigComponents = this.resourcesService.extractComponentsFromModule(module, true); } private loadRuleNodeComponents(ruleChainType: RuleChainType, config?: RequestConfig): Observable> { diff --git a/ui-ngx/src/app/core/services/resources.service.ts b/ui-ngx/src/app/core/services/resources.service.ts index 38bb343aae..dcc6c3f8d0 100644 --- a/ui-ngx/src/app/core/services/resources.service.ts +++ b/ui-ngx/src/app/core/services/resources.service.ts @@ -51,7 +51,7 @@ export interface ModulesWithComponents { standaloneComponents: ɵComponentDef[]; } -export type ComponentsSelectorMap = Record>; +export type ComponentsSelectorMap = Record>; export const flatModulesWithComponents = (modulesWithComponentsList: ModulesWithComponents[]): ModulesWithComponents => { const modulesWithComponents: ModulesWithComponents = { @@ -265,7 +265,7 @@ export class ResourcesService { ); } - public extractComponentsFromModule(module: any, isCamelCaseSelector = false): ComponentsSelectorMap { + public extractComponentsFromModule(module: any, isCamelCaseSelector = false): ComponentsSelectorMap { const modulesWithComponents = this.extractModulesWithComponents(module); const componentMap = {}; From 76b5719e745f4425bc0ed8bb67822ab77bb43093 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 20 Jan 2025 12:28:23 +0200 Subject: [PATCH 078/108] UI: Remove systemRuleNodeConfigComponents register --- ui-ngx/src/app/core/http/rule-chain.service.ts | 10 +++------- .../home/pages/rulechain/rule-node-config.component.ts | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/ui-ngx/src/app/core/http/rule-chain.service.ts b/ui-ngx/src/app/core/http/rule-chain.service.ts index c7b7c04842..749fb39e72 100644 --- a/ui-ngx/src/app/core/http/rule-chain.service.ts +++ b/ui-ngx/src/app/core/http/rule-chain.service.ts @@ -53,7 +53,6 @@ export class RuleChainService { private ruleNodeComponentsMap: Map> = new Map>(); private ruleNodeConfigComponents: {[directive: string]: Type} = {}; - private systemRuleNodeConfigComponents: {[directive: string]: Type} = {}; constructor( private http: HttpClient, @@ -129,10 +128,7 @@ export class RuleChainService { } } - public getRuleNodeConfigComponent(directive: string, isSystemComponent = false): Type { - if (isSystemComponent) { - return this.systemRuleNodeConfigComponents[directive]; - } + public getRuleNodeConfigComponent(directive: string): Type { return this.ruleNodeConfigComponents[directive]; } @@ -185,7 +181,7 @@ export class RuleChainService { } public registerSystemRuleNodeConfigModule(module: any) { - this.systemRuleNodeConfigComponents = this.resourcesService.extractComponentsFromModule(module, true); + Object.assign(this.ruleNodeConfigComponents, this.resourcesService.extractComponentsFromModule(module, true)); } private loadRuleNodeComponents(ruleChainType: RuleChainType, config?: RequestConfig): Observable> { @@ -219,7 +215,7 @@ export class RuleChainService { Observable { 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[] = []; diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-config.component.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-config.component.ts index f6ed94991c..0a5057342f 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-config.component.ts +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-config.component.ts @@ -212,7 +212,7 @@ export class RuleNodeConfigComponent implements ControlValueAccessor, OnDestroy this.changeScriptSubscription = null; } this.definedConfigContainer.clear(); - const component = this.ruleChainService.getRuleNodeConfigComponent(this.nodeDefinition.configDirective, !this.nodeDefinition.uiResources?.length); + const component = this.ruleChainService.getRuleNodeConfigComponent(this.nodeDefinition.configDirective); this.definedConfigComponentRef = this.definedConfigContainer.createComponent(component); this.definedConfigComponent = this.definedConfigComponentRef.instance; this.definedConfigComponent.ruleNodeId = this.ruleNodeId; From c3270e31f817de1281ab1192940595fc81af5af8 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 20 Jan 2025 12:34:31 +0200 Subject: [PATCH 079/108] UI: Add generic type to extractComponentsFromModule --- ui-ngx/src/app/core/services/resources.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/core/services/resources.service.ts b/ui-ngx/src/app/core/services/resources.service.ts index dcc6c3f8d0..72cea30a1a 100644 --- a/ui-ngx/src/app/core/services/resources.service.ts +++ b/ui-ngx/src/app/core/services/resources.service.ts @@ -267,9 +267,9 @@ export class ResourcesService { public extractComponentsFromModule(module: any, isCamelCaseSelector = false): ComponentsSelectorMap { const modulesWithComponents = this.extractModulesWithComponents(module); - const componentMap = {}; + const componentMap: ComponentsSelectorMap = {}; - const processComponents = (components: Array<ɵComponentDef>) => { + const processComponents = (components: Array<ɵComponentDef>) => { components.forEach(item => { let selector = extractSelectorFromComponent(item); if (isCamelCaseSelector) { From 39ded70eec0edc2f912dfedc6873ff12b78b1637 Mon Sep 17 00:00:00 2001 From: Artem Barysh Date: Mon, 20 Jan 2025 12:38:21 +0200 Subject: [PATCH 080/108] Replaced isEqualTo(null) with isNull() --- .../thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java index 334fa5b633..3ab260dea8 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java @@ -103,7 +103,7 @@ public class TbRabbitMqNodeTest { assertThat(config.getExchangeNamePattern()).isEqualTo(""); assertThat(config.getRoutingKeyPattern()).isEqualTo(""); assertThat(config.getMessageProperties()).isNull(); - assertThat(config.getHost()).isEqualTo(null); + assertThat(config.getHost()).isNull(); assertThat(config.getPort()).isEqualTo(ConnectionFactory.DEFAULT_AMQP_PORT); assertThat(config.getVirtualHost()).isEqualTo(ConnectionFactory.DEFAULT_VHOST); assertThat(config.getUsername()).isEqualTo(ConnectionFactory.DEFAULT_USER); From b898ca4d15bbb057fd61406afc4b6b2843e73bcf Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 20 Jan 2025 17:36:07 +0200 Subject: [PATCH 081/108] UI: Add time unit selector and improved save ts rule node --- .../action/timeseries-config.component.html | 60 +++--- .../action/timeseries-config.component.ts | 1 - .../common/common-rule-node-config.module.ts | 7 +- .../common/time-unit-input.component.html | 43 ++++ .../common/time-unit-input.component.ts | 184 ++++++++++++++++++ .../assets/locale/locale.constant-en_US.json | 7 +- 6 files changed, 264 insertions(+), 38 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.ts diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.html index 31e827d331..aa70914fa9 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.html @@ -16,35 +16,33 @@ -->
    - - rule-node-config.default-ttl - - - help - - - {{ 'rule-node-config.default-ttl-required' | translate }} - - - {{ 'rule-node-config.min-default-ttl-message' | translate }} - - -
    -
    - - {{ 'rule-node-config.use-server-ts' | translate }} - -
    -
    - - {{ 'rule-node-config.skip-latest-persistence' | translate }} - -
    -
    +
    + + + rule-node-config.advanced-settings + + + + + help + + +
    + + {{ 'rule-node-config.use-server-ts' | translate }} + +
    +
    +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.ts index d480ec4d39..0c5283f54a 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.ts @@ -38,7 +38,6 @@ export class TimeseriesConfigComponent extends RuleNodeConfigurationComponent { protected onConfigurationSet(configuration: RuleNodeConfiguration) { this.timeseriesConfigForm = this.fb.group({ defaultTTL: [configuration ? configuration.defaultTTL : null, [Validators.required, Validators.min(0)]], - skipLatestPersistence: [configuration ? configuration.skipLatestPersistence : false, []], useServerTs: [configuration ? configuration.useServerTs : false, []] }); } diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/common-rule-node-config.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/common-rule-node-config.module.ts index c72b9901c9..b6370be318 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/common-rule-node-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/common-rule-node-config.module.ts @@ -33,6 +33,7 @@ import { RelationsQueryConfigOldComponent } from './relations-query-config-old.c import { SelectAttributesComponent } from './select-attributes.component'; import { AlarmStatusSelectComponent } from './alarm-status-select.component'; import { ExampleHintComponent } from './example-hint.component'; +import { TimeUnitInputComponent } from './time-unit-input.component'; @NgModule({ declarations: [ @@ -50,7 +51,8 @@ import { ExampleHintComponent } from './example-hint.component'; RelationsQueryConfigOldComponent, SelectAttributesComponent, AlarmStatusSelectComponent, - ExampleHintComponent + ExampleHintComponent, + TimeUnitInputComponent ], imports: [ CommonModule, @@ -72,7 +74,8 @@ import { ExampleHintComponent } from './example-hint.component'; RelationsQueryConfigOldComponent, SelectAttributesComponent, AlarmStatusSelectComponent, - ExampleHintComponent + ExampleHintComponent, + TimeUnitInputComponent ] }) diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.html new file mode 100644 index 0000000000..79caab4cb7 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.html @@ -0,0 +1,43 @@ + +
    + + {{ labelText }} + +
    + +
    + + {{ requiredText }} + + + {{ minErrorText }} + + + {{ maxErrorText }} + +
    + + rule-node-config.units + + @for (timeUnit of timeUnits; track timeUnit) { + {{ timeUnitTranslations.get(timeUnit) | translate }} + } + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.ts new file mode 100644 index 0000000000..3dc0c00124 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.ts @@ -0,0 +1,184 @@ +/// +/// 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, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; +import { + AbstractControl, + ControlValueAccessor, + FormBuilder, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, Validators +} from '@angular/forms'; +import { TimeUnit, timeUnitTranslations } from '../rule-node-config.models'; +import { isDefinedAndNotNull, isNumeric } from '@core/utils'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { coerceBoolean, coerceNumber } from '@shared/decorators/coercion'; +import { DAY, HOUR, MINUTE, SECOND } from '@shared/models/time/time.models'; + +interface TimeUnitInputModel { + time: number; + timeUnit: TimeUnit +} + +@Component({ + selector: 'tb-time-unit-input', + templateUrl: './time-unit-input.component.html', + providers: [{ + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => TimeUnitInputComponent), + multi: true + },{ + provide: NG_VALIDATORS, + useExisting: forwardRef(() => TimeUnitInputComponent), + multi: true + }] +}) +export class TimeUnitInputComponent implements ControlValueAccessor, Validator, OnInit { + + @Input() + labelText: string; + + @Input() + @coerceBoolean() + required: boolean; + + @Input() + requiredText: string; + + @Input() + minErrorText: string; + + @Input() + @coerceNumber() + maxTime: number; + + @Input() + maxErrorText: string; + + timeUnits = Object.values(TimeUnit).filter(item => item !== TimeUnit.MILLISECONDS) as TimeUnit[]; + + timeUnitTranslations = timeUnitTranslations; + + timeInputForm = this.fb.group({ + time: [0, Validators.min(0)], + timeUnit: [TimeUnit.SECONDS] + }); + + private timeIntervalsInSec = new Map([ + [TimeUnit.DAYS, DAY/SECOND], + [TimeUnit.HOURS, HOUR/SECOND], + [TimeUnit.MINUTES, MINUTE/SECOND], + [TimeUnit.SECONDS, SECOND/SECOND], + ]); + + private modelValue: number; + + private propagateChange: (value: any) => void = () => {}; + + constructor(private fb: FormBuilder, + private destroyRef: DestroyRef) { + } + + ngOnInit() { + if(this.required || this.maxTime) { + const timeControl = this.timeInputForm.get('time'); + const validators = []; + if (this.required) { + validators.push(Validators.required); + } + if (this.maxTime) { + validators.push((control: AbstractControl) => + Validators.max(Math.floor(this.maxTime / this.timeIntervalsInSec.get(this.timeInputForm.get('timeUnit').value)))(control) + ); + } + + timeControl.setValidators(validators); + timeControl.updateValueAndValidity({ emitEvent: false }); + } + + this.timeInputForm.get('timeUnit').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.timeInputForm.get('time').updateValueAndValidity({onlySelf: true}); + this.timeInputForm.get('time').markAsTouched({onlySelf: true}); + }); + + this.timeInputForm.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(value => { + this.updatedModel(value); + }); + } + + registerOnChange(fn: any) { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any) { + } + + setDisabledState(isDisabled: boolean) { + if (isDisabled) { + this.timeInputForm.disable({emitEvent: false}); + } else { + this.timeInputForm.enable({emitEvent: false}); + } + } + + writeValue(sec: number) { + if (sec !== this.modelValue) { + if (isDefinedAndNotNull(sec) && isNumeric(sec) && Number(sec) !== 0) { + this.timeInputForm.patchValue(this.parseTime(sec), {emitEvent: false}); + this.modelValue = sec; + } else { + this.timeInputForm.patchValue({ + time: 0, + timeUnit: TimeUnit.SECONDS + }, {emitEvent: false}); + this.modelValue = 0; + } + } + } + + validate(): ValidationErrors | null { + return this.timeInputForm.valid ? null : { + timeInput: false + }; + } + + private updatedModel(value: Partial) { + const time = value.time * this.timeIntervalsInSec.get(value.timeUnit); + if (this.modelValue !== time) { + this.modelValue = time; + this.propagateChange(time); + } + } + + private parseTime(value: number): TimeUnitInputModel { + for (const [timeUnit, timeValue] of this.timeIntervalsInSec) { + const calc = value / timeValue; + if (Number.isInteger(calc)) { + return { + time: calc, + timeUnit: timeUnit + } + } + } + } + +} diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 2fb67adbba..b6aa720a53 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -4541,7 +4541,7 @@ "originator-entity": "Entity by name pattern", "clone-message": "Clone message", "transform": "Transform", - "default-ttl": "Default TTL in seconds", + "default-ttl": "Default TTL", "default-ttl-required": "Default TTL is required.", "default-ttl-hint": "Rule node will fetch Time-to-Live (TTL) value from the message metadata. If no value is present, it defaults to the TTL specified in the configuration. If the value is set to 0, the TTL from the tenant profile configuration will be applied.", "default-ttl-zero-hint": "TTL will not be applied if its value is set to 0.", @@ -4906,9 +4906,7 @@ "general-pattern-hint": "Use ${metadataKey} for value from metadata, $[messageKey] for value from message body.", "alarm-severity-pattern-hint": "Use ${metadataKey} for value from metadata, $[messageKey] for value from message body. Alarm severity should be system (CRITICAL, MAJOR etc.)", "output-node-name-hint": "The 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.", - "skip-latest-persistence": "Skip latest persistence", - "skip-latest-persistence-hint": "Rule node will not update values for incoming keys for the latest time series data. Useful for highly loaded use-cases to decrease the pressure on the DB.", - "use-server-ts": "Use server ts", + "use-server-ts": "Use server timestamp", "use-server-ts-hint": "Rule node will use the timestamp of message processing instead of the timestamp from the message. Useful for all sorts of sequential processing if you merge messages from multiple sources (devices, assets, etc).", "kv-map-pattern-hint": "All input fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", "kv-map-single-pattern-hint": "Input field support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", @@ -5067,6 +5065,7 @@ "request-timeout-required": "Request timeout is required", "request-timeout-min": "Min request timeout is 0", "request-timeout-hint": "The amount of time to wait in seconds for the request to complete before giving up and timing out. A value of 0 means infinity, and is not recommended.", + "units": "Units", "tell-failure-aws-lambda": "Tell Failure if AWS Lambda function execution raises exception", "tell-failure-aws-lambda-hint": "Rule node forces failure of message processing if AWS Lambda function execution raises exception.", "key-val": { From 996b8997fdb6df1f2f3109a585b4866d0ca25be8 Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Tue, 21 Jan 2025 11:14:22 +0200 Subject: [PATCH 082/108] Save time series strategies: simplify SQL upgrade script --- .../main/data/upgrade/basic/schema_update.sql | 78 +++++++------------ 1 file changed, 27 insertions(+), 51 deletions(-) diff --git a/application/src/main/data/upgrade/basic/schema_update.sql b/application/src/main/data/upgrade/basic/schema_update.sql index 6aaf8c3dbd..31833112ad 100644 --- a/application/src/main/data/upgrade/basic/schema_update.sql +++ b/application/src/main/data/upgrade/basic/schema_update.sql @@ -221,60 +221,36 @@ DO $$ WHERE table_name = 'rule_node' ) THEN - -- CREATE JSON validation function - CREATE OR REPLACE FUNCTION is_valid_jsonb(input text) - RETURNS boolean - LANGUAGE plpgsql - AS $func$ - DECLARE - dummy JSONB; - BEGIN - dummy := input::jsonb; - RETURN true; - EXCEPTION - WHEN others THEN - RETURN false; - END; - $func$; - UPDATE rule_node - SET configuration = CASE - -- Case 1: If configuration is NULL, invalid JSON, or not a JSON object - set default configuration - WHEN configuration IS NULL - OR NOT is_valid_jsonb(configuration) - OR jsonb_typeof(configuration::jsonb) <> 'object' - THEN jsonb_build_object( - 'defaultTTL', 0, - 'useServerTs', false, - 'persistenceSettings', jsonb_build_object('type', 'ON_EVERY_MESSAGE') - ) - -- Case 2: If a valid JSON object with persistenceSettings (rule node was already upgraded) - leave unchanged - WHEN configuration::jsonb ? 'persistenceSettings' - THEN configuration::jsonb - -- Case 3: If a valid JSON object without persistenceSettings and skipLatestPersistence = 'true' (string 'true' or boolean true) - set latest to SKIP - WHEN configuration::jsonb ->> 'skipLatestPersistence' = 'true' - THEN (configuration::jsonb - 'skipLatestPersistence') - || jsonb_build_object( - 'persistenceSettings', jsonb_build_object( - 'type', 'ADVANCED', - 'timeseries', jsonb_build_object('type', 'ON_EVERY_MESSAGE'), - 'latest', jsonb_build_object('type', 'SKIP'), - 'webSockets', jsonb_build_object('type', 'ON_EVERY_MESSAGE') - ) - ) - -- Case 4: If a valid JSON object without persistenceSettings and skipLatestPersistence not 'true' (everything else) - set all to ON_EVERY_MESSAGE - ELSE (configuration::jsonb - 'skipLatestPersistence') - || jsonb_build_object( - 'persistenceSettings', jsonb_build_object( - 'type', 'ON_EVERY_MESSAGE' - ) - ) - END::text, + SET configuration = ( + (configuration::jsonb - 'skipLatestPersistence') + || jsonb_build_object( + 'persistenceSettings', jsonb_build_object( + 'type', 'ADVANCED', + 'timeseries', jsonb_build_object('type', 'ON_EVERY_MESSAGE'), + 'latest', jsonb_build_object('type', 'SKIP'), + 'webSockets', jsonb_build_object('type', 'ON_EVERY_MESSAGE') + ) + ) + )::text, configuration_version = 1 - WHERE type = 'org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode' AND configuration_version = 0; + WHERE type = 'org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode' + AND configuration_version = 0 + AND configuration::jsonb ->> 'skipLatestPersistence' = 'true'; - -- Drop the helper function - DROP FUNCTION is_valid_jsonb(text); + UPDATE rule_node + SET configuration = ( + (configuration::jsonb - 'skipLatestPersistence') + || jsonb_build_object( + 'persistenceSettings', jsonb_build_object( + 'type', 'ON_EVERY_MESSAGE' + ) + ) + )::text, + configuration_version = 1 + WHERE type = 'org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode' + AND configuration_version = 0 + AND (configuration::jsonb ->> 'skipLatestPersistence' != 'true' OR configuration::jsonb ->> 'skipLatestPersistence' IS NULL); END IF; END; From f419a6e438382e79eb759fb4a6a0b9ef7a14fdc6 Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Tue, 21 Jan 2025 11:17:05 +0200 Subject: [PATCH 083/108] Save time series strategies: remove unnecessary check in Java upgrade script --- .../thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java index 422065f81f..7a32ee47c1 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java @@ -193,9 +193,6 @@ public class TbMsgTimeseriesNode implements TbNode { boolean hasChanges = false; switch (fromVersion) { case 0: - if (oldConfiguration.has("persistenceSettings") && !oldConfiguration.has("skipLatestPersistence")) { - break; - } hasChanges = true; JsonNode skipLatestPersistence = oldConfiguration.get("skipLatestPersistence"); if (skipLatestPersistence != null && "true".equals(skipLatestPersistence.asText())) { From 39e47cd484c7e0afb6d642b88c3f388728c037f2 Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Tue, 21 Jan 2025 14:26:08 +0200 Subject: [PATCH 084/108] Save time series strategies: refactor boolean flags in TimeseriesSaveRequest.java to SaveActions nested record --- .../DefaultTbEntityViewService.java | 4 +- .../DefaultTelemetrySubscriptionService.java | 19 ++++--- .../DefaultTbEntityViewServiceTest.java | 4 +- ...faultTelemetrySubscriptionServiceTest.java | 28 +++------- .../engine/api/TimeseriesSaveRequest.java | 33 +++++------- .../engine/api/TimeseriesSaveRequestTest.java | 26 +++++++-- .../engine/telemetry/TbMsgTimeseriesNode.java | 54 +++++++------------ .../rule/engine/math/TbMathNodeTest.java | 4 +- .../telemetry/TbMsgTimeseriesNodeTest.java | 42 ++++----------- 9 files changed, 86 insertions(+), 128 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java index ee807da750..3ff1387103 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java @@ -348,9 +348,7 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen .tenantId(entityView.getTenantId()) .entityId(entityId) .entries(latestValues) - .saveTimeseries(false) - .saveLatest(true) - .sendWsUpdate(true) + .saveActions(TimeseriesSaveRequest.SaveActions.LATEST_AND_WS) .callback(new FutureCallback() { @Override public void onSuccess(@Nullable Void tmp) { diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java index dbd7967989..ab95ec6cba 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java @@ -118,10 +118,10 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer EntityId entityId = request.getEntityId(); checkInternalEntity(entityId); boolean sysTenant = TenantId.SYS_TENANT_ID.equals(tenantId) || tenantId == null; - if (sysTenant || !request.isSaveTimeseries() || apiUsageStateService.getApiUsageState(tenantId).isDbStorageEnabled()) { + if (sysTenant || !request.getSaveActions().saveTimeseries() || apiUsageStateService.getApiUsageState(tenantId).isDbStorageEnabled()) { KvUtils.validate(request.getEntries(), valueNoXssValidation); ListenableFuture future = saveTimeseriesInternal(request); - if (request.isSaveTimeseries()) { + if (request.getSaveActions().saveTimeseries()) { FutureCallback callback = getApiUsageCallback(tenantId, request.getCustomerId(), sysTenant, request.getCallback()); Futures.addCallback(future, callback, tsCallBackExecutor); } @@ -134,22 +134,23 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer public ListenableFuture saveTimeseriesInternal(TimeseriesSaveRequest request) { TenantId tenantId = request.getTenantId(); EntityId entityId = request.getEntityId(); + TimeseriesSaveRequest.SaveActions saveActions = request.getSaveActions(); ListenableFuture saveFuture; - if (request.isSaveTimeseries() && request.isSaveLatest()) { + if (saveActions.saveTimeseries() && saveActions.saveLatest()) { saveFuture = tsService.save(tenantId, entityId, request.getEntries(), request.getTtl()); - } else if (request.isSaveLatest()) { + } else if (saveActions.saveLatest()) { saveFuture = Futures.transform(tsService.saveLatest(tenantId, entityId, request.getEntries()), result -> 0, MoreExecutors.directExecutor()); - } else if (request.isSaveTimeseries()) { + } else if (saveActions.saveTimeseries()) { saveFuture = tsService.saveWithoutLatest(tenantId, entityId, request.getEntries(), request.getTtl()); } else { saveFuture = Futures.immediateFuture(0); } addMainCallback(saveFuture, request.getCallback()); - if (request.isSendWsUpdate()) { + if (saveActions.sendWsUpdate()) { addWsCallback(saveFuture, success -> onTimeSeriesUpdate(tenantId, entityId, request.getEntries())); } - if (request.isSaveLatest()) { + if (saveActions.saveLatest()) { copyLatestToEntityViews(tenantId, entityId, request.getEntries()); } return saveFuture; @@ -236,9 +237,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer .tenantId(tenantId) .entityId(entityView.getId()) .entries(entityViewLatest) - .saveTimeseries(false) - .saveLatest(true) - .sendWsUpdate(true) + .saveActions(TimeseriesSaveRequest.SaveActions.LATEST_AND_WS) .callback(new FutureCallback<>() { @Override public void onSuccess(@Nullable Void tmp) {} diff --git a/application/src/test/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewServiceTest.java b/application/src/test/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewServiceTest.java index b36890f775..d6f803c1fe 100644 --- a/application/src/test/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewServiceTest.java @@ -93,9 +93,7 @@ class DefaultTbEntityViewServiceTest { .entityId(entityView.getId()) .entries(latest) .ttl(0L) - .saveTimeseries(false) - .saveLatest(true) - .sendWsUpdate(true) + .saveActions(TimeseriesSaveRequest.SaveActions.LATEST_AND_WS) .build(); var actualCopyLatestRequest = captor.getValue(); diff --git a/application/src/test/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionServiceTest.java b/application/src/test/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionServiceTest.java index 8162ef4555..21d3aac607 100644 --- a/application/src/test/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionServiceTest.java @@ -173,9 +173,7 @@ class DefaultTelemetrySubscriptionServiceTest { .entityId(entityId) .entries(sampleTelemetry) .ttl(sampleTtl) - .saveTimeseries(true) - .saveLatest(false) - .sendWsUpdate(false) + .saveActions(new TimeseriesSaveRequest.SaveActions(true, false, false)) .callback(emptyCallback) .build(); @@ -195,9 +193,7 @@ class DefaultTelemetrySubscriptionServiceTest { .entityId(entityId) .entries(sampleTelemetry) .ttl(sampleTtl) - .saveTimeseries(false) - .saveLatest(true) - .sendWsUpdate(true) + .saveActions(TimeseriesSaveRequest.SaveActions.LATEST_AND_WS) .callback(emptyCallback) .build(); @@ -220,9 +216,7 @@ class DefaultTelemetrySubscriptionServiceTest { .entityId(entityId) .entries(sampleTelemetry) .ttl(sampleTtl) - .saveTimeseries(true) - .saveLatest(true) - .sendWsUpdate(true) + .saveActions(TimeseriesSaveRequest.SaveActions.SAVE_ALL) .future(future) .build(); @@ -248,9 +242,7 @@ class DefaultTelemetrySubscriptionServiceTest { .entityId(entityId) .entries(sampleTelemetry) .ttl(sampleTtl) - .saveTimeseries(false) - .saveLatest(true) - .sendWsUpdate(true) + .saveActions(TimeseriesSaveRequest.SaveActions.LATEST_AND_WS) .future(future) .build(); @@ -283,9 +275,7 @@ class DefaultTelemetrySubscriptionServiceTest { .entityId(entityId) .entries(sampleTelemetry) .ttl(sampleTtl) - .saveTimeseries(false) - .saveLatest(true) - .sendWsUpdate(false) + .saveActions(new TimeseriesSaveRequest.SaveActions(false, true, false)) .callback(emptyCallback) .build(); @@ -312,9 +302,7 @@ class DefaultTelemetrySubscriptionServiceTest { .entityId(entityId) .entries(sampleTelemetry) .ttl(sampleTtl) - .saveTimeseries(true) - .saveLatest(false) - .sendWsUpdate(false) + .saveActions(new TimeseriesSaveRequest.SaveActions(true, false, false)) .callback(emptyCallback) .build(); @@ -340,9 +328,7 @@ class DefaultTelemetrySubscriptionServiceTest { .entityId(entityId) .entries(sampleTelemetry) .ttl(sampleTtl) - .saveTimeseries(saveTimeseries) - .saveLatest(saveLatest) - .sendWsUpdate(sendWsUpdate) + .saveActions(new TimeseriesSaveRequest.SaveActions(saveTimeseries, saveLatest, sendWsUpdate)) .callback(emptyCallback) .build(); diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java index 1ad0fd3408..d1122cc70d 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java @@ -38,11 +38,18 @@ public class TimeseriesSaveRequest { private final EntityId entityId; private final List entries; private final long ttl; - private final boolean saveTimeseries; - private final boolean saveLatest; - private final boolean sendWsUpdate; + private final SaveActions saveActions; private final FutureCallback callback; + public record SaveActions(boolean saveTimeseries, boolean saveLatest, boolean sendWsUpdate) { + + public static final SaveActions SAVE_ALL = new SaveActions(true, true, true); + public static final SaveActions WS_ONLY = new SaveActions(false, false, true); + public static final SaveActions LATEST_AND_WS = new SaveActions(false, true, true); + public static final SaveActions SKIP_ALL = new SaveActions(false, false, false); + + } + public static Builder builder() { return new Builder(); } @@ -54,9 +61,7 @@ public class TimeseriesSaveRequest { private EntityId entityId; private List entries; private long ttl; - private boolean saveTimeseries = true; - private boolean saveLatest = true; - private boolean sendWsUpdate = true; + private SaveActions saveActions = SaveActions.SAVE_ALL; private FutureCallback callback; Builder() {} @@ -94,18 +99,8 @@ public class TimeseriesSaveRequest { return this; } - public Builder saveTimeseries(boolean saveTimeseries) { - this.saveTimeseries = saveTimeseries; - return this; - } - - public Builder saveLatest(boolean saveLatest) { - this.saveLatest = saveLatest; - return this; - } - - public Builder sendWsUpdate(boolean sendWsUpdate) { - this.sendWsUpdate = sendWsUpdate; + public Builder saveActions(SaveActions settings) { + this.saveActions = settings; return this; } @@ -129,7 +124,7 @@ public class TimeseriesSaveRequest { } public TimeseriesSaveRequest build() { - return new TimeseriesSaveRequest(tenantId, customerId, entityId, entries, ttl, saveTimeseries, saveLatest, sendWsUpdate, callback); + return new TimeseriesSaveRequest(tenantId, customerId, entityId, entries, ttl, saveActions, callback); } } diff --git a/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequestTest.java b/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequestTest.java index cf05f9905b..38ce10a272 100644 --- a/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequestTest.java +++ b/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequestTest.java @@ -22,12 +22,30 @@ import static org.assertj.core.api.Assertions.assertThat; class TimeseriesSaveRequestTest { @Test - void testBooleanFlagsDefaultToTrue() { + void testDefaultSaveActionsAreSaveAll() { var request = TimeseriesSaveRequest.builder().build(); - assertThat(request.isSaveTimeseries()).isTrue(); - assertThat(request.isSaveLatest()).isTrue(); - assertThat(request.isSendWsUpdate()).isTrue(); + assertThat(request.getSaveActions()).isEqualTo(TimeseriesSaveRequest.SaveActions.SAVE_ALL); + } + + @Test + void testSaveActionsSaveAll() { + assertThat(TimeseriesSaveRequest.SaveActions.SAVE_ALL).isEqualTo(new TimeseriesSaveRequest.SaveActions(true, true, true)); + } + + @Test + void testSaveActionsWsOnly() { + assertThat(TimeseriesSaveRequest.SaveActions.WS_ONLY).isEqualTo(new TimeseriesSaveRequest.SaveActions(false, false, true)); + } + + @Test + void testSaveActionsLatestAndWs() { + assertThat(TimeseriesSaveRequest.SaveActions.LATEST_AND_WS).isEqualTo(new TimeseriesSaveRequest.SaveActions(false, true, true)); + } + + @Test + void testSaveActionsSkipAll() { + assertThat(TimeseriesSaveRequest.SaveActions.SKIP_ALL).isEqualTo(new TimeseriesSaveRequest.SaveActions(false, false, false)); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java index 7a32ee47c1..658d635fae 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java @@ -107,13 +107,10 @@ public class TbMsgTimeseriesNode implements TbNode { } long ts = computeTs(msg, config.isUseServerTs()); - PersistenceDecision persistenceDecision = makePersistenceDecision(ts, msg.getOriginator().getId()); - boolean saveTimeseries = persistenceDecision.saveTimeseries(); - boolean saveLatest = persistenceDecision.saveLatest(); - boolean sendWsUpdate = persistenceDecision.sendWsUpdate(); + TimeseriesSaveRequest.SaveActions saveActions = determineSaveActions(ts, msg.getOriginator().getId()); // short-circuit - if (!saveTimeseries && !saveLatest && !sendWsUpdate) { + if (!saveActions.saveTimeseries() && !saveActions.saveLatest() && !saveActions.sendWsUpdate()) { ctx.tellSuccess(msg); return; } @@ -141,9 +138,7 @@ public class TbMsgTimeseriesNode implements TbNode { .entityId(msg.getOriginator()) .entries(tsKvEntryList) .ttl(ttl) - .saveTimeseries(saveTimeseries) - .saveLatest(saveLatest) - .sendWsUpdate(sendWsUpdate) + .saveActions(saveActions) .callback(new TelemetryNodeCallback(ctx, msg)) .build()); } @@ -152,35 +147,26 @@ public class TbMsgTimeseriesNode implements TbNode { return ignoreMetadataTs ? System.currentTimeMillis() : msg.getMetaDataTs(); } - private record PersistenceDecision(boolean saveTimeseries, boolean saveLatest, boolean sendWsUpdate) {} - - private PersistenceDecision makePersistenceDecision(long ts, UUID originatorUuid) { - boolean saveTimeseries; - boolean saveLatest; - boolean sendWsUpdate; - + private TimeseriesSaveRequest.SaveActions determineSaveActions(long ts, UUID originatorUuid) { if (persistenceSettings instanceof OnEveryMessage) { - saveTimeseries = true; - saveLatest = true; - sendWsUpdate = true; - } else if (persistenceSettings instanceof WebSocketsOnly) { - saveTimeseries = false; - saveLatest = false; - sendWsUpdate = true; - } else if (persistenceSettings instanceof Deduplicate deduplicate) { + return TimeseriesSaveRequest.SaveActions.SAVE_ALL; + } + if (persistenceSettings instanceof WebSocketsOnly) { + return TimeseriesSaveRequest.SaveActions.WS_ONLY; + } + if (persistenceSettings instanceof Deduplicate deduplicate) { boolean isFirstMsgInInterval = deduplicate.getDeduplicateStrategy().shouldPersist(ts, originatorUuid); - saveTimeseries = isFirstMsgInInterval; - saveLatest = isFirstMsgInInterval; - sendWsUpdate = isFirstMsgInInterval; - } else if (persistenceSettings instanceof Advanced advanced) { - saveTimeseries = advanced.timeseries().shouldPersist(ts, originatorUuid); - saveLatest = advanced.latest().shouldPersist(ts, originatorUuid); - sendWsUpdate = advanced.webSockets().shouldPersist(ts, originatorUuid); - } else { // should not happen - throw new IllegalArgumentException("Unknown persistence settings type: " + persistenceSettings.getClass().getSimpleName()); + return isFirstMsgInInterval ? TimeseriesSaveRequest.SaveActions.SAVE_ALL : TimeseriesSaveRequest.SaveActions.SKIP_ALL; } - - return new PersistenceDecision(saveTimeseries, saveLatest, sendWsUpdate); + if (persistenceSettings instanceof Advanced advanced) { + return new TimeseriesSaveRequest.SaveActions( + advanced.timeseries().shouldPersist(ts, originatorUuid), + advanced.latest().shouldPersist(ts, originatorUuid), + advanced.webSockets().shouldPersist(ts, originatorUuid) + ); + } + // should not happen + throw new IllegalArgumentException("Unknown persistence settings type: " + persistenceSettings.getClass().getSimpleName()); } @Override diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java index 360fbce5b0..1270c8cf6c 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java @@ -533,7 +533,7 @@ public class TbMathNodeTest { verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); verify(telemetryService, times(1)).saveTimeseries(assertArg(request -> { assertThat(request.getEntries()).size().isOne(); - assertThat(request.isSaveLatest()).isTrue(); + assertThat(request.getSaveActions()).isEqualTo(TimeseriesSaveRequest.SaveActions.SAVE_ALL); })); TbMsg resultMsg = msgCaptor.getValue(); @@ -569,7 +569,7 @@ public class TbMathNodeTest { verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); verify(telemetryService, times(1)).saveTimeseries(assertArg(request -> { assertThat(request.getEntries()).size().isOne(); - assertThat(request.isSaveLatest()).isTrue(); + assertThat(request.getSaveActions()).isEqualTo(TimeseriesSaveRequest.SaveActions.SAVE_ALL); })); TbMsg resultMsg = msgCaptor.getValue(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java index d0727b1a3c..15904ca1ff 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java @@ -207,7 +207,7 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { assertThat(request.getEntityId()).isEqualTo(DEVICE_ID); assertThat(request.getEntries()).usingRecursiveFieldByFieldElementComparatorIgnoringFields("ts").containsExactlyElementsOf(expectedList); assertThat(request.getTtl()).isEqualTo(extractTtlAsSeconds(tenantProfile)); - assertThat(request.isSaveLatest()).isTrue(); + assertThat(request.getSaveActions()).isEqualTo(TimeseriesSaveRequest.SaveActions.SAVE_ALL); assertThat(request.getCallback()).isInstanceOf(TelemetryNodeCallback.class); })); verify(ctxMock).tellSuccess(msg); @@ -264,9 +264,7 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { assertThat(request.getEntityId()).isEqualTo(DEVICE_ID); assertThat(request.getEntries()).containsExactlyElementsOf(expectedList); assertThat(request.getTtl()).isEqualTo(config.getDefaultTTL()); - assertThat(request.isSaveTimeseries()).isTrue(); - assertThat(request.isSaveLatest()).isFalse(); - assertThat(request.isSendWsUpdate()).isTrue(); + assertThat(request.getSaveActions()).isEqualTo(new TimeseriesSaveRequest.SaveActions(true, false, true)); assertThat(request.getCallback()).isInstanceOf(TelemetryNodeCallback.class); })); verify(ctxMock).tellSuccess(msg); @@ -305,7 +303,7 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { assertThat(request.getCustomerId()).isNull(); assertThat(request.getEntityId()).isEqualTo(DEVICE_ID); assertThat(request.getTtl()).isEqualTo(expectedTtl); - assertThat(request.isSaveLatest()).isTrue(); + assertThat(request.getSaveActions()).isEqualTo(TimeseriesSaveRequest.SaveActions.SAVE_ALL); assertThat(request.getCallback()).isInstanceOf(TelemetryNodeCallback.class); })); } @@ -354,9 +352,7 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { .entityId(msg.getOriginator()) .entry(new BasicTsKvEntry(123L, new DoubleDataEntry("temperature", 22.3))) .ttl(extractTtlAsSeconds(tenantProfile)) - .saveTimeseries(true) - .saveLatest(true) - .sendWsUpdate(true) + .saveActions(TimeseriesSaveRequest.SaveActions.SAVE_ALL) .build(); node.onMsg(ctxMock, msg); @@ -391,9 +387,7 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { .entityId(msg.getOriginator()) .entry(new BasicTsKvEntry(123L, new DoubleDataEntry("temperature", 22.3))) .ttl(extractTtlAsSeconds(tenantProfile)) - .saveTimeseries(true) - .saveLatest(true) - .sendWsUpdate(true) + .saveActions(TimeseriesSaveRequest.SaveActions.SAVE_ALL) .build(); node.onMsg(ctxMock, msg); @@ -428,9 +422,7 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { .entityId(msg.getOriginator()) .entry(new BasicTsKvEntry(123L, new DoubleDataEntry("temperature", 22.3))) .ttl(extractTtlAsSeconds(tenantProfile)) - .saveTimeseries(false) - .saveLatest(false) - .sendWsUpdate(true) + .saveActions(TimeseriesSaveRequest.SaveActions.WS_ONLY) .build(); node.onMsg(ctxMock, msg); @@ -469,9 +461,7 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { .entityId(msg.getOriginator()) .entry(new BasicTsKvEntry(123L, new DoubleDataEntry("temperature", 22.3))) .ttl(extractTtlAsSeconds(tenantProfile)) - .saveTimeseries(true) - .saveLatest(true) - .sendWsUpdate(true) + .saveActions(TimeseriesSaveRequest.SaveActions.SAVE_ALL) .build(); node.onMsg(ctxMock, msg); @@ -508,11 +498,7 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { .metaData(new TbMsgMetaData(Map.of("ts", Long.toString(ts1)))) .build()); then(telemetryServiceMock).should().saveTimeseries(assertArg( - actualSaveRequest -> { - assertThat(actualSaveRequest.isSaveTimeseries()).isTrue(); - assertThat(actualSaveRequest.isSaveLatest()).isTrue(); - assertThat(actualSaveRequest.isSendWsUpdate()).isTrue(); - } + actualSaveRequest -> assertThat(actualSaveRequest.getSaveActions()).isEqualTo(TimeseriesSaveRequest.SaveActions.SAVE_ALL) )); clearInvocations(telemetryServiceMock); @@ -524,11 +510,7 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { .metaData(new TbMsgMetaData(Map.of("ts", Long.toString(ts2)))) .build()); then(telemetryServiceMock).should().saveTimeseries(assertArg( - actualSaveRequest -> { - assertThat(actualSaveRequest.isSaveTimeseries()).isTrue(); - assertThat(actualSaveRequest.isSaveLatest()).isFalse(); - assertThat(actualSaveRequest.isSendWsUpdate()).isFalse(); - } + actualSaveRequest -> assertThat(actualSaveRequest.getSaveActions()).isEqualTo(new TimeseriesSaveRequest.SaveActions(true, false, false)) )); clearInvocations(telemetryServiceMock); @@ -540,11 +522,7 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { .metaData(new TbMsgMetaData(Map.of("ts", Long.toString(ts3)))) .build()); then(telemetryServiceMock).should().saveTimeseries(assertArg( - actualSaveRequest -> { - assertThat(actualSaveRequest.isSaveTimeseries()).isTrue(); - assertThat(actualSaveRequest.isSaveLatest()).isTrue(); - assertThat(actualSaveRequest.isSendWsUpdate()).isFalse(); - } + actualSaveRequest -> assertThat(actualSaveRequest.getSaveActions()).isEqualTo(new TimeseriesSaveRequest.SaveActions(true, true, false)) )); } From 148521eddf6bdd95b44ab0986f83f7b8895656d3 Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Tue, 21 Jan 2025 16:10:35 +0200 Subject: [PATCH 085/108] Save time series strategies: use @NotNull annotation instead of manual check --- .../rule/engine/telemetry/TbMsgTimeseriesNode.java | 3 --- .../telemetry/TbMsgTimeseriesNodeConfiguration.java | 2 ++ .../engine/telemetry/TbMsgTimeseriesNodeTest.java | 11 ++++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java index 658d635fae..eb2862a0da 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java @@ -89,9 +89,6 @@ public class TbMsgTimeseriesNode implements TbNode { ctx.addTenantProfileListener(this::onTenantProfileUpdate); onTenantProfileUpdate(ctx.getTenantProfile()); persistenceSettings = config.getPersistenceSettings(); - if (persistenceSettings == null) { - throw new TbNodeException("Persistence settings cannot be null", true); - } } private void onTenantProfileUpdate(TenantProfile tenantProfile) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java index f702ecc6da..fe22e42710 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.Getter; import org.thingsboard.rule.engine.api.NodeConfiguration; @@ -37,6 +38,7 @@ public class TbMsgTimeseriesNodeConfiguration implements NodeConfiguration node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config)))) - .isInstanceOf(TbNodeException.class) - .matches(e -> ((TbNodeException) e).isUnrecoverable()) - .hasMessage("Persistence settings cannot be null"); + assertThatThrownBy(() -> ConstraintValidator.validateFields(config)) + .isInstanceOf(DataValidationException.class) + .hasMessage("Validation error: persistenceSettings must not be null"); } @ParameterizedTest From e009967fa74e58a0da5fd883d4a452f0303fba95 Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Tue, 21 Jan 2025 16:21:28 +0200 Subject: [PATCH 086/108] Save time series strategies: rename SaveActions to Strategy --- .../DefaultTbEntityViewService.java | 2 +- .../DefaultTelemetrySubscriptionService.java | 18 ++++++++--------- .../DefaultTbEntityViewServiceTest.java | 2 +- ...faultTelemetrySubscriptionServiceTest.java | 14 ++++++------- .../engine/api/TimeseriesSaveRequest.java | 20 +++++++++---------- .../engine/api/TimeseriesSaveRequestTest.java | 20 +++++++++---------- .../engine/telemetry/TbMsgTimeseriesNode.java | 16 +++++++-------- .../rule/engine/math/TbMathNodeTest.java | 4 ++-- .../telemetry/TbMsgTimeseriesNodeTest.java | 20 +++++++++---------- 9 files changed, 58 insertions(+), 58 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java index 3ff1387103..2c384aa493 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java @@ -348,7 +348,7 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen .tenantId(entityView.getTenantId()) .entityId(entityId) .entries(latestValues) - .saveActions(TimeseriesSaveRequest.SaveActions.LATEST_AND_WS) + .strategy(TimeseriesSaveRequest.Strategy.LATEST_AND_WS) .callback(new FutureCallback() { @Override public void onSuccess(@Nullable Void tmp) { diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java index ab95ec6cba..cab2f18dc7 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java @@ -118,10 +118,10 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer EntityId entityId = request.getEntityId(); checkInternalEntity(entityId); boolean sysTenant = TenantId.SYS_TENANT_ID.equals(tenantId) || tenantId == null; - if (sysTenant || !request.getSaveActions().saveTimeseries() || apiUsageStateService.getApiUsageState(tenantId).isDbStorageEnabled()) { + if (sysTenant || !request.getStrategy().saveTimeseries() || apiUsageStateService.getApiUsageState(tenantId).isDbStorageEnabled()) { KvUtils.validate(request.getEntries(), valueNoXssValidation); ListenableFuture future = saveTimeseriesInternal(request); - if (request.getSaveActions().saveTimeseries()) { + if (request.getStrategy().saveTimeseries()) { FutureCallback callback = getApiUsageCallback(tenantId, request.getCustomerId(), sysTenant, request.getCallback()); Futures.addCallback(future, callback, tsCallBackExecutor); } @@ -134,23 +134,23 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer public ListenableFuture saveTimeseriesInternal(TimeseriesSaveRequest request) { TenantId tenantId = request.getTenantId(); EntityId entityId = request.getEntityId(); - TimeseriesSaveRequest.SaveActions saveActions = request.getSaveActions(); + TimeseriesSaveRequest.Strategy strategy = request.getStrategy(); ListenableFuture saveFuture; - if (saveActions.saveTimeseries() && saveActions.saveLatest()) { + if (strategy.saveTimeseries() && strategy.saveLatest()) { saveFuture = tsService.save(tenantId, entityId, request.getEntries(), request.getTtl()); - } else if (saveActions.saveLatest()) { + } else if (strategy.saveLatest()) { saveFuture = Futures.transform(tsService.saveLatest(tenantId, entityId, request.getEntries()), result -> 0, MoreExecutors.directExecutor()); - } else if (saveActions.saveTimeseries()) { + } else if (strategy.saveTimeseries()) { saveFuture = tsService.saveWithoutLatest(tenantId, entityId, request.getEntries(), request.getTtl()); } else { saveFuture = Futures.immediateFuture(0); } addMainCallback(saveFuture, request.getCallback()); - if (saveActions.sendWsUpdate()) { + if (strategy.sendWsUpdate()) { addWsCallback(saveFuture, success -> onTimeSeriesUpdate(tenantId, entityId, request.getEntries())); } - if (saveActions.saveLatest()) { + if (strategy.saveLatest()) { copyLatestToEntityViews(tenantId, entityId, request.getEntries()); } return saveFuture; @@ -237,7 +237,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer .tenantId(tenantId) .entityId(entityView.getId()) .entries(entityViewLatest) - .saveActions(TimeseriesSaveRequest.SaveActions.LATEST_AND_WS) + .strategy(TimeseriesSaveRequest.Strategy.LATEST_AND_WS) .callback(new FutureCallback<>() { @Override public void onSuccess(@Nullable Void tmp) {} diff --git a/application/src/test/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewServiceTest.java b/application/src/test/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewServiceTest.java index d6f803c1fe..aa6bfde935 100644 --- a/application/src/test/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewServiceTest.java @@ -93,7 +93,7 @@ class DefaultTbEntityViewServiceTest { .entityId(entityView.getId()) .entries(latest) .ttl(0L) - .saveActions(TimeseriesSaveRequest.SaveActions.LATEST_AND_WS) + .strategy(TimeseriesSaveRequest.Strategy.LATEST_AND_WS) .build(); var actualCopyLatestRequest = captor.getValue(); diff --git a/application/src/test/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionServiceTest.java b/application/src/test/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionServiceTest.java index 21d3aac607..10fdd85504 100644 --- a/application/src/test/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionServiceTest.java @@ -173,7 +173,7 @@ class DefaultTelemetrySubscriptionServiceTest { .entityId(entityId) .entries(sampleTelemetry) .ttl(sampleTtl) - .saveActions(new TimeseriesSaveRequest.SaveActions(true, false, false)) + .strategy(new TimeseriesSaveRequest.Strategy(true, false, false)) .callback(emptyCallback) .build(); @@ -193,7 +193,7 @@ class DefaultTelemetrySubscriptionServiceTest { .entityId(entityId) .entries(sampleTelemetry) .ttl(sampleTtl) - .saveActions(TimeseriesSaveRequest.SaveActions.LATEST_AND_WS) + .strategy(TimeseriesSaveRequest.Strategy.LATEST_AND_WS) .callback(emptyCallback) .build(); @@ -216,7 +216,7 @@ class DefaultTelemetrySubscriptionServiceTest { .entityId(entityId) .entries(sampleTelemetry) .ttl(sampleTtl) - .saveActions(TimeseriesSaveRequest.SaveActions.SAVE_ALL) + .strategy(TimeseriesSaveRequest.Strategy.SAVE_ALL) .future(future) .build(); @@ -242,7 +242,7 @@ class DefaultTelemetrySubscriptionServiceTest { .entityId(entityId) .entries(sampleTelemetry) .ttl(sampleTtl) - .saveActions(TimeseriesSaveRequest.SaveActions.LATEST_AND_WS) + .strategy(TimeseriesSaveRequest.Strategy.LATEST_AND_WS) .future(future) .build(); @@ -275,7 +275,7 @@ class DefaultTelemetrySubscriptionServiceTest { .entityId(entityId) .entries(sampleTelemetry) .ttl(sampleTtl) - .saveActions(new TimeseriesSaveRequest.SaveActions(false, true, false)) + .strategy(new TimeseriesSaveRequest.Strategy(false, true, false)) .callback(emptyCallback) .build(); @@ -302,7 +302,7 @@ class DefaultTelemetrySubscriptionServiceTest { .entityId(entityId) .entries(sampleTelemetry) .ttl(sampleTtl) - .saveActions(new TimeseriesSaveRequest.SaveActions(true, false, false)) + .strategy(new TimeseriesSaveRequest.Strategy(true, false, false)) .callback(emptyCallback) .build(); @@ -328,7 +328,7 @@ class DefaultTelemetrySubscriptionServiceTest { .entityId(entityId) .entries(sampleTelemetry) .ttl(sampleTtl) - .saveActions(new TimeseriesSaveRequest.SaveActions(saveTimeseries, saveLatest, sendWsUpdate)) + .strategy(new TimeseriesSaveRequest.Strategy(saveTimeseries, saveLatest, sendWsUpdate)) .callback(emptyCallback) .build(); diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java index d1122cc70d..fb667fbfb2 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequest.java @@ -38,15 +38,15 @@ public class TimeseriesSaveRequest { private final EntityId entityId; private final List entries; private final long ttl; - private final SaveActions saveActions; + private final Strategy strategy; private final FutureCallback callback; - public record SaveActions(boolean saveTimeseries, boolean saveLatest, boolean sendWsUpdate) { + public record Strategy(boolean saveTimeseries, boolean saveLatest, boolean sendWsUpdate) { - public static final SaveActions SAVE_ALL = new SaveActions(true, true, true); - public static final SaveActions WS_ONLY = new SaveActions(false, false, true); - public static final SaveActions LATEST_AND_WS = new SaveActions(false, true, true); - public static final SaveActions SKIP_ALL = new SaveActions(false, false, false); + public static final Strategy SAVE_ALL = new Strategy(true, true, true); + public static final Strategy WS_ONLY = new Strategy(false, false, true); + public static final Strategy LATEST_AND_WS = new Strategy(false, true, true); + public static final Strategy SKIP_ALL = new Strategy(false, false, false); } @@ -61,7 +61,7 @@ public class TimeseriesSaveRequest { private EntityId entityId; private List entries; private long ttl; - private SaveActions saveActions = SaveActions.SAVE_ALL; + private Strategy strategy = Strategy.SAVE_ALL; private FutureCallback callback; Builder() {} @@ -99,8 +99,8 @@ public class TimeseriesSaveRequest { return this; } - public Builder saveActions(SaveActions settings) { - this.saveActions = settings; + public Builder strategy(Strategy strategy) { + this.strategy = strategy; return this; } @@ -124,7 +124,7 @@ public class TimeseriesSaveRequest { } public TimeseriesSaveRequest build() { - return new TimeseriesSaveRequest(tenantId, customerId, entityId, entries, ttl, saveActions, callback); + return new TimeseriesSaveRequest(tenantId, customerId, entityId, entries, ttl, strategy, callback); } } diff --git a/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequestTest.java b/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequestTest.java index 38ce10a272..321892991e 100644 --- a/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequestTest.java +++ b/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/TimeseriesSaveRequestTest.java @@ -22,30 +22,30 @@ import static org.assertj.core.api.Assertions.assertThat; class TimeseriesSaveRequestTest { @Test - void testDefaultSaveActionsAreSaveAll() { + void testDefaultSaveStrategyIsSaveAll() { var request = TimeseriesSaveRequest.builder().build(); - assertThat(request.getSaveActions()).isEqualTo(TimeseriesSaveRequest.SaveActions.SAVE_ALL); + assertThat(request.getStrategy()).isEqualTo(TimeseriesSaveRequest.Strategy.SAVE_ALL); } @Test - void testSaveActionsSaveAll() { - assertThat(TimeseriesSaveRequest.SaveActions.SAVE_ALL).isEqualTo(new TimeseriesSaveRequest.SaveActions(true, true, true)); + void testSaveAllStrategy() { + assertThat(TimeseriesSaveRequest.Strategy.SAVE_ALL).isEqualTo(new TimeseriesSaveRequest.Strategy(true, true, true)); } @Test - void testSaveActionsWsOnly() { - assertThat(TimeseriesSaveRequest.SaveActions.WS_ONLY).isEqualTo(new TimeseriesSaveRequest.SaveActions(false, false, true)); + void testWsOnlyStrategy() { + assertThat(TimeseriesSaveRequest.Strategy.WS_ONLY).isEqualTo(new TimeseriesSaveRequest.Strategy(false, false, true)); } @Test - void testSaveActionsLatestAndWs() { - assertThat(TimeseriesSaveRequest.SaveActions.LATEST_AND_WS).isEqualTo(new TimeseriesSaveRequest.SaveActions(false, true, true)); + void testLatestAndWsStrategy() { + assertThat(TimeseriesSaveRequest.Strategy.LATEST_AND_WS).isEqualTo(new TimeseriesSaveRequest.Strategy(false, true, true)); } @Test - void testSaveActionsSkipAll() { - assertThat(TimeseriesSaveRequest.SaveActions.SKIP_ALL).isEqualTo(new TimeseriesSaveRequest.SaveActions(false, false, false)); + void testSkipAllStrategy() { + assertThat(TimeseriesSaveRequest.Strategy.SKIP_ALL).isEqualTo(new TimeseriesSaveRequest.Strategy(false, false, false)); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java index eb2862a0da..53261bf7dd 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java @@ -104,10 +104,10 @@ public class TbMsgTimeseriesNode implements TbNode { } long ts = computeTs(msg, config.isUseServerTs()); - TimeseriesSaveRequest.SaveActions saveActions = determineSaveActions(ts, msg.getOriginator().getId()); + TimeseriesSaveRequest.Strategy strategy = determineSaveActions(ts, msg.getOriginator().getId()); // short-circuit - if (!saveActions.saveTimeseries() && !saveActions.saveLatest() && !saveActions.sendWsUpdate()) { + if (!strategy.saveTimeseries() && !strategy.saveLatest() && !strategy.sendWsUpdate()) { ctx.tellSuccess(msg); return; } @@ -135,7 +135,7 @@ public class TbMsgTimeseriesNode implements TbNode { .entityId(msg.getOriginator()) .entries(tsKvEntryList) .ttl(ttl) - .saveActions(saveActions) + .strategy(strategy) .callback(new TelemetryNodeCallback(ctx, msg)) .build()); } @@ -144,19 +144,19 @@ public class TbMsgTimeseriesNode implements TbNode { return ignoreMetadataTs ? System.currentTimeMillis() : msg.getMetaDataTs(); } - private TimeseriesSaveRequest.SaveActions determineSaveActions(long ts, UUID originatorUuid) { + private TimeseriesSaveRequest.Strategy determineSaveActions(long ts, UUID originatorUuid) { if (persistenceSettings instanceof OnEveryMessage) { - return TimeseriesSaveRequest.SaveActions.SAVE_ALL; + return TimeseriesSaveRequest.Strategy.SAVE_ALL; } if (persistenceSettings instanceof WebSocketsOnly) { - return TimeseriesSaveRequest.SaveActions.WS_ONLY; + return TimeseriesSaveRequest.Strategy.WS_ONLY; } if (persistenceSettings instanceof Deduplicate deduplicate) { boolean isFirstMsgInInterval = deduplicate.getDeduplicateStrategy().shouldPersist(ts, originatorUuid); - return isFirstMsgInInterval ? TimeseriesSaveRequest.SaveActions.SAVE_ALL : TimeseriesSaveRequest.SaveActions.SKIP_ALL; + return isFirstMsgInInterval ? TimeseriesSaveRequest.Strategy.SAVE_ALL : TimeseriesSaveRequest.Strategy.SKIP_ALL; } if (persistenceSettings instanceof Advanced advanced) { - return new TimeseriesSaveRequest.SaveActions( + return new TimeseriesSaveRequest.Strategy( advanced.timeseries().shouldPersist(ts, originatorUuid), advanced.latest().shouldPersist(ts, originatorUuid), advanced.webSockets().shouldPersist(ts, originatorUuid) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java index 1270c8cf6c..63432afc37 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java @@ -533,7 +533,7 @@ public class TbMathNodeTest { verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); verify(telemetryService, times(1)).saveTimeseries(assertArg(request -> { assertThat(request.getEntries()).size().isOne(); - assertThat(request.getSaveActions()).isEqualTo(TimeseriesSaveRequest.SaveActions.SAVE_ALL); + assertThat(request.getStrategy()).isEqualTo(TimeseriesSaveRequest.Strategy.SAVE_ALL); })); TbMsg resultMsg = msgCaptor.getValue(); @@ -569,7 +569,7 @@ public class TbMathNodeTest { verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); verify(telemetryService, times(1)).saveTimeseries(assertArg(request -> { assertThat(request.getEntries()).size().isOne(); - assertThat(request.getSaveActions()).isEqualTo(TimeseriesSaveRequest.SaveActions.SAVE_ALL); + assertThat(request.getStrategy()).isEqualTo(TimeseriesSaveRequest.Strategy.SAVE_ALL); })); TbMsg resultMsg = msgCaptor.getValue(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java index 71236c97be..02c19ed5fc 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeTest.java @@ -208,7 +208,7 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { assertThat(request.getEntityId()).isEqualTo(DEVICE_ID); assertThat(request.getEntries()).usingRecursiveFieldByFieldElementComparatorIgnoringFields("ts").containsExactlyElementsOf(expectedList); assertThat(request.getTtl()).isEqualTo(extractTtlAsSeconds(tenantProfile)); - assertThat(request.getSaveActions()).isEqualTo(TimeseriesSaveRequest.SaveActions.SAVE_ALL); + assertThat(request.getStrategy()).isEqualTo(TimeseriesSaveRequest.Strategy.SAVE_ALL); assertThat(request.getCallback()).isInstanceOf(TelemetryNodeCallback.class); })); verify(ctxMock).tellSuccess(msg); @@ -265,7 +265,7 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { assertThat(request.getEntityId()).isEqualTo(DEVICE_ID); assertThat(request.getEntries()).containsExactlyElementsOf(expectedList); assertThat(request.getTtl()).isEqualTo(config.getDefaultTTL()); - assertThat(request.getSaveActions()).isEqualTo(new TimeseriesSaveRequest.SaveActions(true, false, true)); + assertThat(request.getStrategy()).isEqualTo(new TimeseriesSaveRequest.Strategy(true, false, true)); assertThat(request.getCallback()).isInstanceOf(TelemetryNodeCallback.class); })); verify(ctxMock).tellSuccess(msg); @@ -304,7 +304,7 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { assertThat(request.getCustomerId()).isNull(); assertThat(request.getEntityId()).isEqualTo(DEVICE_ID); assertThat(request.getTtl()).isEqualTo(expectedTtl); - assertThat(request.getSaveActions()).isEqualTo(TimeseriesSaveRequest.SaveActions.SAVE_ALL); + assertThat(request.getStrategy()).isEqualTo(TimeseriesSaveRequest.Strategy.SAVE_ALL); assertThat(request.getCallback()).isInstanceOf(TelemetryNodeCallback.class); })); } @@ -353,7 +353,7 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { .entityId(msg.getOriginator()) .entry(new BasicTsKvEntry(123L, new DoubleDataEntry("temperature", 22.3))) .ttl(extractTtlAsSeconds(tenantProfile)) - .saveActions(TimeseriesSaveRequest.SaveActions.SAVE_ALL) + .strategy(TimeseriesSaveRequest.Strategy.SAVE_ALL) .build(); node.onMsg(ctxMock, msg); @@ -388,7 +388,7 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { .entityId(msg.getOriginator()) .entry(new BasicTsKvEntry(123L, new DoubleDataEntry("temperature", 22.3))) .ttl(extractTtlAsSeconds(tenantProfile)) - .saveActions(TimeseriesSaveRequest.SaveActions.SAVE_ALL) + .strategy(TimeseriesSaveRequest.Strategy.SAVE_ALL) .build(); node.onMsg(ctxMock, msg); @@ -423,7 +423,7 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { .entityId(msg.getOriginator()) .entry(new BasicTsKvEntry(123L, new DoubleDataEntry("temperature", 22.3))) .ttl(extractTtlAsSeconds(tenantProfile)) - .saveActions(TimeseriesSaveRequest.SaveActions.WS_ONLY) + .strategy(TimeseriesSaveRequest.Strategy.WS_ONLY) .build(); node.onMsg(ctxMock, msg); @@ -462,7 +462,7 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { .entityId(msg.getOriginator()) .entry(new BasicTsKvEntry(123L, new DoubleDataEntry("temperature", 22.3))) .ttl(extractTtlAsSeconds(tenantProfile)) - .saveActions(TimeseriesSaveRequest.SaveActions.SAVE_ALL) + .strategy(TimeseriesSaveRequest.Strategy.SAVE_ALL) .build(); node.onMsg(ctxMock, msg); @@ -499,7 +499,7 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { .metaData(new TbMsgMetaData(Map.of("ts", Long.toString(ts1)))) .build()); then(telemetryServiceMock).should().saveTimeseries(assertArg( - actualSaveRequest -> assertThat(actualSaveRequest.getSaveActions()).isEqualTo(TimeseriesSaveRequest.SaveActions.SAVE_ALL) + actualSaveRequest -> assertThat(actualSaveRequest.getStrategy()).isEqualTo(TimeseriesSaveRequest.Strategy.SAVE_ALL) )); clearInvocations(telemetryServiceMock); @@ -511,7 +511,7 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { .metaData(new TbMsgMetaData(Map.of("ts", Long.toString(ts2)))) .build()); then(telemetryServiceMock).should().saveTimeseries(assertArg( - actualSaveRequest -> assertThat(actualSaveRequest.getSaveActions()).isEqualTo(new TimeseriesSaveRequest.SaveActions(true, false, false)) + actualSaveRequest -> assertThat(actualSaveRequest.getStrategy()).isEqualTo(new TimeseriesSaveRequest.Strategy(true, false, false)) )); clearInvocations(telemetryServiceMock); @@ -523,7 +523,7 @@ public class TbMsgTimeseriesNodeTest extends AbstractRuleNodeUpgradeTest { .metaData(new TbMsgMetaData(Map.of("ts", Long.toString(ts3)))) .build()); then(telemetryServiceMock).should().saveTimeseries(assertArg( - actualSaveRequest -> assertThat(actualSaveRequest.getSaveActions()).isEqualTo(new TimeseriesSaveRequest.SaveActions(true, true, false)) + actualSaveRequest -> assertThat(actualSaveRequest.getStrategy()).isEqualTo(new TimeseriesSaveRequest.Strategy(true, true, false)) )); } From a2095636a07af68c083e4fe3bb06e92089082589 Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Wed, 22 Jan 2025 10:56:36 +0200 Subject: [PATCH 087/108] Save time series strategies: add max deduplication interval validation --- .../strategy/DeduplicatePersistenceStrategy.java | 6 ++++-- .../strategy/DeduplicatePersistenceStrategyTest.java | 9 ++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategy.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategy.java index e4eb982861..513523f8f3 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategy.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategy.java @@ -28,14 +28,16 @@ import java.util.UUID; final class DeduplicatePersistenceStrategy implements PersistenceStrategy { private static final int MIN_DEDUPLICATION_INTERVAL_SECS = 1; + private static final int MAX_DEDUPLICATION_INTERVAL_SECS = (int) Duration.ofDays(1L).toSeconds(); private final long deduplicationIntervalMillis; private final LoadingCache> deduplicationCache; @JsonCreator public DeduplicatePersistenceStrategy(@JsonProperty("deduplicationIntervalSecs") int deduplicationIntervalSecs) { - if (deduplicationIntervalSecs < MIN_DEDUPLICATION_INTERVAL_SECS) { - throw new IllegalArgumentException("Deduplication interval must be at least " + MIN_DEDUPLICATION_INTERVAL_SECS + " second(s), was " + deduplicationIntervalSecs + " second(s)"); + if (deduplicationIntervalSecs < MIN_DEDUPLICATION_INTERVAL_SECS || deduplicationIntervalSecs > MAX_DEDUPLICATION_INTERVAL_SECS) { + throw new IllegalArgumentException("Deduplication interval must be at least " + MIN_DEDUPLICATION_INTERVAL_SECS + " second(s) " + + "and at most " + MAX_DEDUPLICATION_INTERVAL_SECS + " second(s), was " + deduplicationIntervalSecs + " second(s)"); } deduplicationIntervalMillis = Duration.ofSeconds(deduplicationIntervalSecs).toMillis(); deduplicationCache = Caffeine.newBuilder() diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategyTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategyTest.java index 3f57a7f3df..8aeda400b4 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategyTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategyTest.java @@ -39,7 +39,14 @@ class DeduplicatePersistenceStrategyTest { void shouldThrowWhenDeduplicationIntervalIsLessThanOneSecond() { assertThatThrownBy(() -> new DeduplicatePersistenceStrategy(0)) .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("Deduplication interval must be at least 1 second(s), was 0 second(s)"); + .hasMessageContaining("Deduplication interval must be at least 1 second(s) and at most 86400 second(s), was 0 second(s)"); + } + + @Test + void shouldThrowWhenDeduplicationIntervalIsMoreThan24Hours() { + assertThatThrownBy(() -> new DeduplicatePersistenceStrategy(86401)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Deduplication interval must be at least 1 second(s) and at most 86400 second(s), was 86401 second(s)"); } @Test From 349554f93870ac6b4e34bfa1a0657f8d56759d92 Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Wed, 22 Jan 2025 12:17:26 +0200 Subject: [PATCH 088/108] Save time series strategies: dynamically calculate max number of deduplication intervals --- .../engine/telemetry/TbMsgTimeseriesNode.java | 4 +- .../DeduplicatePersistenceStrategy.java | 14 ++++- .../DeduplicatePersistenceStrategyTest.java | 55 +++++++++++++++++++ 3 files changed, 70 insertions(+), 3 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java index 53261bf7dd..232b8a3d36 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java @@ -104,7 +104,7 @@ public class TbMsgTimeseriesNode implements TbNode { } long ts = computeTs(msg, config.isUseServerTs()); - TimeseriesSaveRequest.Strategy strategy = determineSaveActions(ts, msg.getOriginator().getId()); + TimeseriesSaveRequest.Strategy strategy = determineSaveStrategy(ts, msg.getOriginator().getId()); // short-circuit if (!strategy.saveTimeseries() && !strategy.saveLatest() && !strategy.sendWsUpdate()) { @@ -144,7 +144,7 @@ public class TbMsgTimeseriesNode implements TbNode { return ignoreMetadataTs ? System.currentTimeMillis() : msg.getMetaDataTs(); } - private TimeseriesSaveRequest.Strategy determineSaveActions(long ts, UUID originatorUuid) { + private TimeseriesSaveRequest.Strategy determineSaveStrategy(long ts, UUID originatorUuid) { if (persistenceSettings instanceof OnEveryMessage) { return TimeseriesSaveRequest.Strategy.SAVE_ALL; } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategy.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategy.java index 513523f8f3..601328c304 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategy.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategy.java @@ -30,6 +30,9 @@ final class DeduplicatePersistenceStrategy implements PersistenceStrategy { private static final int MIN_DEDUPLICATION_INTERVAL_SECS = 1; private static final int MAX_DEDUPLICATION_INTERVAL_SECS = (int) Duration.ofDays(1L).toSeconds(); + private static final int MAX_TOTAL_INTERVALS_DURATION_SECS = (int) Duration.ofDays(2L).toSeconds(); + private static final int MAX_NUMBER_OF_INTERVALS = 100; + private final long deduplicationIntervalMillis; private final LoadingCache> deduplicationCache; @@ -43,10 +46,19 @@ final class DeduplicatePersistenceStrategy implements PersistenceStrategy { deduplicationCache = Caffeine.newBuilder() .softValues() .expireAfterAccess(Duration.ofSeconds(deduplicationIntervalSecs * 10L)) - .maximumSize(20L) + .maximumSize(calculateMaxNumberOfDeduplicationIntervals(deduplicationIntervalSecs)) .build(__ -> Sets.newConcurrentHashSet()); } + /** + * Calculates the maximum number of deduplication intervals we will store in the cache. + * We limit retention to two days to avoid stale data and cap it at 100 intervals to manage memory usage. + */ + private static long calculateMaxNumberOfDeduplicationIntervals(int deduplicationIntervalSecs) { + int numberOfDeduplicationIntervals = MAX_TOTAL_INTERVALS_DURATION_SECS / deduplicationIntervalSecs; + return Math.min(numberOfDeduplicationIntervals, MAX_NUMBER_OF_INTERVALS); + } + @JsonProperty("deduplicationIntervalSecs") public long getDeduplicationIntervalSecs() { return Duration.ofMillis(deduplicationIntervalMillis).toSeconds(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategyTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategyTest.java index 8aeda400b4..1a6bdc831d 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategyTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategyTest.java @@ -15,10 +15,14 @@ */ package org.thingsboard.rule.engine.telemetry.strategy; +import com.github.benmanes.caffeine.cache.LoadingCache; +import com.github.benmanes.caffeine.cache.Policy; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; import java.time.Duration; +import java.util.Set; import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; @@ -49,6 +53,57 @@ class DeduplicatePersistenceStrategyTest { .hasMessageContaining("Deduplication interval must be at least 1 second(s) and at most 86400 second(s), was 86401 second(s)"); } + @Test + void shouldNotAllowMoreThan100DeduplicationIntervals() { + // GIVEN + int deduplicationIntervalSecs = 1; // min deduplication interval duration + + // WHEN + strategy = new DeduplicatePersistenceStrategy(deduplicationIntervalSecs); + + // THEN + var deduplicationCache = (LoadingCache>) ReflectionTestUtils.getField(strategy, "deduplicationCache"); + + assertThat(deduplicationCache.policy().eviction()) + .isPresent() + .map(Policy.Eviction::getMaximum) + .hasValue(100L); + } + + @Test + void shouldCalculateMaxIntervalsAsTwoDaysDividedByIntervalDuration() { + // GIVEN + int deduplicationIntervalSecs = (int) Duration.ofHours(1L).toSeconds(); + + // WHEN + strategy = new DeduplicatePersistenceStrategy(deduplicationIntervalSecs); + + // THEN + var deduplicationCache = (LoadingCache>) ReflectionTestUtils.getField(strategy, "deduplicationCache"); + + assertThat(deduplicationCache.policy().eviction()) + .isPresent() + .map(Policy.Eviction::getMaximum) + .hasValue(48L); + } + + @Test + void shouldKeepAtLeastTwoDeduplicationIntervals() { + // GIVEN + int deduplicationIntervalSecs = (int) Duration.ofDays(1L).toSeconds(); // max deduplication interval duration + + // WHEN + strategy = new DeduplicatePersistenceStrategy(deduplicationIntervalSecs); + + // THEN + var deduplicationCache = (LoadingCache>) ReflectionTestUtils.getField(strategy, "deduplicationCache"); + + assertThat(deduplicationCache.policy().eviction()) + .isPresent() + .map(Policy.Eviction::getMaximum) + .hasValue(2L); + } + @Test void shouldReturnTrueForFirstCallInInterval() { long ts = 1_000_000L; From 0131358d3ba3b5e37c203c9500f7fc7ef477335e Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Wed, 22 Jan 2025 15:10:56 +0200 Subject: [PATCH 089/108] Save time series strategies: dynamically calculate interval expire after access --- .../DeduplicatePersistenceStrategy.java | 18 ++++++- .../DeduplicatePersistenceStrategyTest.java | 51 +++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategy.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategy.java index 601328c304..868e0d8ca4 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategy.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategy.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; import com.google.common.collect.Sets; +import com.google.common.primitives.Longs; import java.time.Duration; import java.util.Set; @@ -30,6 +31,10 @@ final class DeduplicatePersistenceStrategy implements PersistenceStrategy { private static final int MIN_DEDUPLICATION_INTERVAL_SECS = 1; private static final int MAX_DEDUPLICATION_INTERVAL_SECS = (int) Duration.ofDays(1L).toSeconds(); + private static final long MIN_INTERVAL_EXPIRY_MILLIS = Duration.ofMinutes(10L).toMillis(); + private static final int INTERVAL_EXPIRY_FACTOR = 10; + private static final long MAX_INTERVAL_EXPIRY_MILLIS = Duration.ofDays(2L).toMillis(); + private static final int MAX_TOTAL_INTERVALS_DURATION_SECS = (int) Duration.ofDays(2L).toSeconds(); private static final int MAX_NUMBER_OF_INTERVALS = 100; @@ -45,11 +50,22 @@ final class DeduplicatePersistenceStrategy implements PersistenceStrategy { deduplicationIntervalMillis = Duration.ofSeconds(deduplicationIntervalSecs).toMillis(); deduplicationCache = Caffeine.newBuilder() .softValues() - .expireAfterAccess(Duration.ofSeconds(deduplicationIntervalSecs * 10L)) + .expireAfterAccess(calculateExpireAfterAccess(deduplicationIntervalSecs)) .maximumSize(calculateMaxNumberOfDeduplicationIntervals(deduplicationIntervalSecs)) .build(__ -> Sets.newConcurrentHashSet()); } + /** + * Calculates the expire-after-access duration. By default, we keep each deduplication interval + * alive for 10 “iterations” (interval duration × 10). However, we never let this drop below + * 10 minutes to ensure adequate retention for small intervals, nor exceed 48 hours to prevent + * storing stale data in memory. + */ + private static Duration calculateExpireAfterAccess(int deduplicationIntervalSecs) { + long desiredExpiryMillis = Duration.ofSeconds(deduplicationIntervalSecs).toMillis() * INTERVAL_EXPIRY_FACTOR; + return Duration.ofMillis(Longs.constrainToRange(desiredExpiryMillis, MIN_INTERVAL_EXPIRY_MILLIS, MAX_INTERVAL_EXPIRY_MILLIS)); + } + /** * Calculates the maximum number of deduplication intervals we will store in the cache. * We limit retention to two days to avoid stale data and cap it at 100 intervals to manage memory usage. diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategyTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategyTest.java index 1a6bdc831d..1ae2b367a5 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategyTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/strategy/DeduplicatePersistenceStrategyTest.java @@ -53,6 +53,57 @@ class DeduplicatePersistenceStrategyTest { .hasMessageContaining("Deduplication interval must be at least 1 second(s) and at most 86400 second(s), was 86401 second(s)"); } + @Test + void shouldUseAtLeastTenMinutesForExpireAfterAccess() { + // GIVEN + int deduplicationIntervalSecs = 1; // min deduplication interval duration + + // WHEN + strategy = new DeduplicatePersistenceStrategy(deduplicationIntervalSecs); + + // THEN + var deduplicationCache = (LoadingCache>) ReflectionTestUtils.getField(strategy, "deduplicationCache"); + + assertThat(deduplicationCache.policy().expireAfterAccess()) + .isPresent() + .map(Policy.FixedExpiration::getExpiresAfter) + .hasValue(Duration.ofMinutes(10L)); + } + + @Test + void shouldCalculateExpireAfterAccessAsIntervalDurationMultipliedByTen() { + // GIVEN + int deduplicationIntervalSecs = (int) Duration.ofHours(1L).toSeconds(); // max deduplication interval duration + + // WHEN + strategy = new DeduplicatePersistenceStrategy(deduplicationIntervalSecs); + + // THEN + var deduplicationCache = (LoadingCache>) ReflectionTestUtils.getField(strategy, "deduplicationCache"); + + assertThat(deduplicationCache.policy().expireAfterAccess()) + .isPresent() + .map(Policy.FixedExpiration::getExpiresAfter) + .hasValue(Duration.ofHours(10L)); + } + + @Test + void shouldUseAtMostTwoDaysForExpireAfterAccess() { + // GIVEN + int deduplicationIntervalSecs = (int) Duration.ofDays(1L).toSeconds(); // max deduplication interval duration + + // WHEN + strategy = new DeduplicatePersistenceStrategy(deduplicationIntervalSecs); + + // THEN + var deduplicationCache = (LoadingCache>) ReflectionTestUtils.getField(strategy, "deduplicationCache"); + + assertThat(deduplicationCache.policy().expireAfterAccess()) + .isPresent() + .map(Policy.FixedExpiration::getExpiresAfter) + .hasValue(Duration.ofDays(2L)); + } + @Test void shouldNotAllowMoreThan100DeduplicationIntervals() { // GIVEN From 2d1ead5f544520c4a35c7149e19399f181dd4d65 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 22 Jan 2025 15:43:59 +0200 Subject: [PATCH 090/108] UI: Add persistence settings in save ts rule node --- .../action/action-rule-node-config.module.ts | 10 +- ...ced-persistence-setting-row.component.html | 39 ++++++ ...anced-persistence-setting-row.component.ts | 114 ++++++++++++++++++ ...dvanced-persistence-setting.component.html | 31 +++++ .../advanced-persistence-setting.component.ts | 83 +++++++++++++ .../action/timeseries-config.component.html | 40 ++++++ .../action/timeseries-config.component.ts | 104 ++++++++++++++-- .../action/timeseries-config.models.ts | 78 ++++++++++++ .../common/time-unit-input.component.html | 3 +- .../assets/locale/locale.constant-en_US.json | 19 +++ 10 files changed, 511 insertions(+), 10 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/advanced-persistence-setting-row.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/advanced-persistence-setting-row.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/advanced-persistence-setting.component.html create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/advanced-persistence-setting.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.models.ts diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/action-rule-node-config.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/action-rule-node-config.module.ts index 17307cb341..caec009629 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/action-rule-node-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/action-rule-node-config.module.ts @@ -42,6 +42,12 @@ import { DeleteAttributesConfigComponent } from './delete-attributes-config.comp import { MathFunctionConfigComponent } from './math-function-config.component'; import { DeviceStateConfigComponent } from './device-state-config.component'; import { SendRestApiCallReplyConfigComponent } from './send-rest-api-call-reply-config.component'; +import { + AdvancedPersistenceSettingComponent +} from '@home/components/rule-node/action/advanced-persistence-setting.component'; +import { + AdvancedPersistenceSettingRowComponent +} from '@home/components/rule-node/action/advanced-persistence-setting-row.component'; @NgModule({ declarations: [ @@ -67,7 +73,9 @@ import { SendRestApiCallReplyConfigComponent } from './send-rest-api-call-reply- PushToEdgeConfigComponent, PushToCloudConfigComponent, MathFunctionConfigComponent, - DeviceStateConfigComponent + DeviceStateConfigComponent, + AdvancedPersistenceSettingComponent, + AdvancedPersistenceSettingRowComponent, ], imports: [ CommonModule, diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/advanced-persistence-setting-row.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/advanced-persistence-setting-row.component.html new file mode 100644 index 0000000000..038fe41bb2 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/advanced-persistence-setting-row.component.html @@ -0,0 +1,39 @@ + +
    +
    {{ title }}
    + + rule-node-config.save-time-series.strategy + + @for (strategy of persistenceStrategies; track strategy) { + {{ PersistenceTypeTranslationMap.get(strategy) | translate }} + } + + + @if(persistenceSettingRowForm.get('type').value === PersistenceType.DEDUPLICATE) { + + + } +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/advanced-persistence-setting-row.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/advanced-persistence-setting-row.component.ts new file mode 100644 index 0000000000..64ea045f8f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/advanced-persistence-setting-row.component.ts @@ -0,0 +1,114 @@ +/// +/// 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, forwardRef, Input } from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator +} from '@angular/forms'; +import { + AdvancedPersistenceConfig, + defaultAdvancedPersistenceConfig, + maxDeduplicateTimeSecs, + PersistenceType, + PersistenceTypeTranslationMap +} from '@home/components/rule-node/action/timeseries-config.models'; +import { isDefinedAndNotNull } from '@core/utils'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; + +@Component({ + selector: 'tb-advanced-persistence-setting-row', + templateUrl: './advanced-persistence-setting-row.component.html', + providers: [{ + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => AdvancedPersistenceSettingRowComponent), + multi: true + },{ + provide: NG_VALIDATORS, + useExisting: forwardRef(() => AdvancedPersistenceSettingRowComponent), + multi: true + }] +}) +export class AdvancedPersistenceSettingRowComponent implements ControlValueAccessor, Validator { + + @Input() + title: string; + + persistenceSettingRowForm = this.fb.group({ + type: [defaultAdvancedPersistenceConfig.type], + deduplicationIntervalSecs: [{value: 60, disabled: true}] + }); + + PersistenceType = PersistenceType; + persistenceStrategies = [PersistenceType.ON_EVERY_MESSAGE, PersistenceType.DEDUPLICATE, PersistenceType.SKIP]; + PersistenceTypeTranslationMap = PersistenceTypeTranslationMap; + + maxDeduplicateTime = maxDeduplicateTimeSecs; + + private propagateChange: (value: any) => void = () => {}; + + constructor(private fb: FormBuilder) { + this.persistenceSettingRowForm.get('type').valueChanges.pipe( + takeUntilDestroyed() + ).subscribe(() => this.updatedValidation()); + + this.persistenceSettingRowForm.valueChanges.pipe( + takeUntilDestroyed() + ).subscribe((value) => this.propagateChange(value)); + } + + registerOnChange(fn: any) { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any) { + } + + setDisabledState(isDisabled: boolean) { + if (isDisabled) { + this.persistenceSettingRowForm.disable({emitEvent: false}); + } else { + this.persistenceSettingRowForm.enable({emitEvent: false}); + this.updatedValidation(); + } + } + + validate(): ValidationErrors | null { + return this.persistenceSettingRowForm.valid ? null : { + persistenceSettingRow: false + }; + } + + writeValue(value: AdvancedPersistenceConfig) { + if (isDefinedAndNotNull(value)) { + this.persistenceSettingRowForm.patchValue(value, {emitEvent: false}); + } else { + this.persistenceSettingRowForm.patchValue(defaultAdvancedPersistenceConfig); + } + } + + private updatedValidation() { + if (this.persistenceSettingRowForm.get('type').value === PersistenceType.DEDUPLICATE) { + this.persistenceSettingRowForm.get('deduplicationIntervalSecs').enable({emitEvent: false}); + } else { + this.persistenceSettingRowForm.get('deduplicationIntervalSecs').disable({emitEvent: false}) + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/advanced-persistence-setting.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/advanced-persistence-setting.component.html new file mode 100644 index 0000000000..eb0f05bec2 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/advanced-persistence-setting.component.html @@ -0,0 +1,31 @@ + +
    + + + +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/advanced-persistence-setting.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/advanced-persistence-setting.component.ts new file mode 100644 index 0000000000..5c9930fbb7 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/advanced-persistence-setting.component.ts @@ -0,0 +1,83 @@ +/// +/// 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 { + ControlValueAccessor, + FormBuilder, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator +} from '@angular/forms'; +import { Component, forwardRef } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { AdvancedPersistenceStrategy } from '@home/components/rule-node/action/timeseries-config.models'; + +@Component({ + selector: 'tb-advanced-persistence-settings', + templateUrl: './advanced-persistence-setting.component.html', + providers: [{ + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => AdvancedPersistenceSettingComponent), + multi: true + },{ + provide: NG_VALIDATORS, + useExisting: forwardRef(() => AdvancedPersistenceSettingComponent), + multi: true + }] +}) +export class AdvancedPersistenceSettingComponent implements ControlValueAccessor, Validator { + + persistenceForm = this.fb.group({ + timeseries: [null], + latest: [null], + webSockets: [null] + }); + + private propagateChange: (value: any) => void = () => {}; + + constructor(private fb: FormBuilder) { + this.persistenceForm.valueChanges.pipe( + takeUntilDestroyed() + ).subscribe(value => this.propagateChange(value)); + } + + registerOnChange(fn: any) { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any) { + } + + setDisabledState(isDisabled: boolean) { + if (isDisabled) { + this.persistenceForm.disable({emitEvent: false}); + } else { + this.persistenceForm.enable({emitEvent: false}); + } + } + + validate(): ValidationErrors | null { + return this.persistenceForm.valid ? null : { + persistenceForm: false + }; + } + + writeValue(value: AdvancedPersistenceStrategy) { + this.persistenceForm.patchValue(value, {emitEvent: false}); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.html index aa70914fa9..47655c1285 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.html @@ -16,6 +16,46 @@ -->
    +
    +
    +
    + rule-node-config.save-time-series.persistence-settings +
    + + {{ 'rule-node-config.basic-mode' | translate}} + {{ 'rule-node-config.advanced-mode' | translate }} + +
    + @if(!timeseriesConfigForm.get('persistenceSettings.isAdvanced').value) { + + rule-node-config.save-time-series.strategy + + @for (strategy of persistenceStrategies; track strategy) { + {{ PersistenceTypeTranslationMap.get(strategy) | translate }} + } + + + + @if(timeseriesConfigForm.get('persistenceSettings.type').value === PersistenceType.DEDUPLICATE) { + + + } + } + @else { + + } +
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.ts index 0c5283f54a..a834d0d43f 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.ts @@ -15,8 +15,18 @@ /// import { Component } from '@angular/core'; -import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; -import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { RuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; +import { + defaultAdvancedPersistenceStrategy, + maxDeduplicateTimeSecs, + PersistenceSettings, + PersistenceSettingsForm, + PersistenceType, + PersistenceTypeTranslationMap, + TimeseriesNodeConfiguration, + TimeseriesNodeConfigurationForm +} from '@home/components/rule-node/action/timeseries-config.models'; @Component({ selector: 'tb-action-node-timeseries-config', @@ -25,20 +35,98 @@ import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/m }) export class TimeseriesConfigComponent extends RuleNodeConfigurationComponent { - timeseriesConfigForm: UntypedFormGroup; + timeseriesConfigForm: FormGroup; - constructor(private fb: UntypedFormBuilder) { + PersistenceType = PersistenceType; + persistenceStrategies = [PersistenceType.ON_EVERY_MESSAGE, PersistenceType.DEDUPLICATE, PersistenceType.WEBSOCKETS_ONLY]; + PersistenceTypeTranslationMap = PersistenceTypeTranslationMap; + + maxDeduplicateTime = maxDeduplicateTimeSecs + + constructor(private fb: FormBuilder) { super(); } - protected configForm(): UntypedFormGroup { + protected configForm(): FormGroup { return this.timeseriesConfigForm; } - protected onConfigurationSet(configuration: RuleNodeConfiguration) { + protected validatorTriggers(): string[] { + return ['persistenceSettings.isAdvanced', 'persistenceSettings.type']; + } + + protected prepareInputConfig(config: TimeseriesNodeConfiguration): TimeseriesNodeConfigurationForm { + let persistenceSettings: PersistenceSettingsForm; + if (config?.persistenceSettings) { + const isAdvanced = config?.persistenceSettings?.type === PersistenceType.ADVANCED; + persistenceSettings = { + ...config.persistenceSettings, + isAdvanced: isAdvanced, + type: isAdvanced ? PersistenceType.ON_EVERY_MESSAGE : config.persistenceSettings.type, + advanced: isAdvanced ? config.persistenceSettings : defaultAdvancedPersistenceStrategy + } + } else { + persistenceSettings = { + type: PersistenceType.ON_EVERY_MESSAGE, + isAdvanced: false, + deduplicationIntervalSecs: 10, + advanced: defaultAdvancedPersistenceStrategy + }; + } + return { + ...config, + persistenceSettings: persistenceSettings + } + } + + protected prepareOutputConfig(config: TimeseriesNodeConfigurationForm): TimeseriesNodeConfiguration { + let persistenceSettings: PersistenceSettings; + if (config.persistenceSettings.isAdvanced) { + persistenceSettings = { + ...config.persistenceSettings.advanced, + type: PersistenceType.ADVANCED + }; + } else { + persistenceSettings = { + type: config.persistenceSettings.type, + deduplicationIntervalSecs: config.persistenceSettings?.deduplicationIntervalSecs + }; + } + return { + ...config, + persistenceSettings + }; + } + + protected onConfigurationSet(config: TimeseriesNodeConfigurationForm) { this.timeseriesConfigForm = this.fb.group({ - defaultTTL: [configuration ? configuration.defaultTTL : null, [Validators.required, Validators.min(0)]], - useServerTs: [configuration ? configuration.useServerTs : false, []] + persistenceSettings: this.fb.group({ + isAdvanced: [config?.persistenceSettings?.isAdvanced ?? false], + type: [config?.persistenceSettings?.type ?? PersistenceType.ON_EVERY_MESSAGE], + deduplicationIntervalSecs: [ + {value: config?.persistenceSettings?.deduplicationIntervalSecs ?? 10, disabled: true}, + [Validators.required, Validators.max(maxDeduplicateTimeSecs)] + ], + advanced: [{value: null, disabled: true}] + }), + defaultTTL: [config?.defaultTTL ?? null, [Validators.required, Validators.min(0)]], + useServerTs: [config?.useServerTs ?? false] }); } + + protected updateValidators(emitEvent: boolean, _trigger?: string) { + const persistenceForm = this.timeseriesConfigForm.get('persistenceSettings') as FormGroup; + const isAdvanced: boolean = persistenceForm.get('isAdvanced').value; + const type: PersistenceType = persistenceForm.get('type').value; + if (!isAdvanced && type === PersistenceType.DEDUPLICATE) { + persistenceForm.get('deduplicationIntervalSecs').enable({emitEvent}); + } else { + persistenceForm.get('deduplicationIntervalSecs').disable({emitEvent}); + } + if (isAdvanced) { + persistenceForm.get('advanced').enable({emitEvent}); + } else { + persistenceForm.get('advanced').disable({emitEvent}); + } + } } diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.models.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.models.ts new file mode 100644 index 0000000000..241787d511 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.models.ts @@ -0,0 +1,78 @@ +/// +/// 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 { DAY, SECOND } from '@shared/models/time/time.models'; + +export const maxDeduplicateTimeSecs = DAY / SECOND + 1; + +export interface TimeseriesNodeConfiguration { + persistenceSettings: PersistenceSettings; + defaultTTL: number; + useServerTs: boolean; +} + +export interface TimeseriesNodeConfigurationForm extends Omit { + persistenceSettings: PersistenceSettingsForm +} + +export type PersistenceSettings = BasicPersistenceSettings & Partial & Partial; + +export type PersistenceSettingsForm = Omit & { + isAdvanced: boolean; + advanced?: Partial; + type: PersistenceType; +}; + +export enum PersistenceType { + ON_EVERY_MESSAGE = 'ON_EVERY_MESSAGE', + DEDUPLICATE = 'DEDUPLICATE', + WEBSOCKETS_ONLY = 'WEBSOCKETS_ONLY', + ADVANCED = 'ADVANCED', + SKIP = 'SKIP' +} + +export const PersistenceTypeTranslationMap = new Map([ + [PersistenceType.ON_EVERY_MESSAGE, 'rule-node-config.save-time-series.strategy-type.every-message'], + [PersistenceType.DEDUPLICATE, 'rule-node-config.save-time-series.strategy-type.deduplicate'], + [PersistenceType.WEBSOCKETS_ONLY, 'rule-node-config.save-time-series.strategy-type.web-sockets-only'], + [PersistenceType.SKIP, 'rule-node-config.save-time-series.strategy-type.skip'], +]) + +export interface BasicPersistenceSettings { + type: PersistenceType; +} + +export interface DeduplicatePersistenceStrategy extends BasicPersistenceSettings{ + deduplicationIntervalSecs: number; +} + +export interface AdvancedPersistenceStrategy extends BasicPersistenceSettings{ + timeseries: AdvancedPersistenceConfig; + latest: AdvancedPersistenceConfig; + webSockets: AdvancedPersistenceConfig; +} + +export type AdvancedPersistenceConfig = WithOptional; + +export const defaultAdvancedPersistenceConfig: AdvancedPersistenceConfig = { + type: PersistenceType.ON_EVERY_MESSAGE +} + +export const defaultAdvancedPersistenceStrategy: Omit = { + timeseries: defaultAdvancedPersistenceConfig, + latest: defaultAdvancedPersistenceConfig, + webSockets: defaultAdvancedPersistenceConfig, +} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.html index 79caab4cb7..1fd0a60159 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.html @@ -16,12 +16,13 @@ -->
    - + {{ labelText }}
    + {{ requiredText }} diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index b6aa720a53..f19336d6ad 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -5068,6 +5068,25 @@ "units": "Units", "tell-failure-aws-lambda": "Tell Failure if AWS Lambda function execution raises exception", "tell-failure-aws-lambda-hint": "Rule node forces failure of message processing if AWS Lambda function execution raises exception.", + "basic-mode": "Basic", + "advanced-mode": "Advanced", + "save-time-series": { + "persistence-settings": "Persistence settings", + "persistence-settings-hint": "Persistence settings hint", + "strategy": "Strategy", + "deduplication-interval": "Deduplication interval", + "deduplication-interval-required": "Deduplication interval is required", + "deduplication-interval-min-max-range": "Deduplication interval should be at least 1 second and at most 1 day", + "strategy-type": { + "every-message": "On every message", + "skip": "Skip", + "deduplicate": "Deduplicate", + "web-sockets-only": "WebSockets only" + }, + "time-series": "Time series", + "latest": "Latest", + "web-sockets": "WebSockets" + }, "key-val": { "key": "Key", "value": "Value", From 6ba8aa90ab54b10e20353449c460da6e1486b6cf Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 22 Jan 2025 15:49:25 +0200 Subject: [PATCH 091/108] UI: Clear rule node components import --- .../rule-node/action/action-rule-node-config.module.ts | 4 ++-- .../rule-node/enrichment/enrichment-rule-node-core.module.ts | 4 ++-- .../rule-node/external/external-rule-node-config.module.ts | 4 ++-- .../rule-node/filter/filter-rule-node-config.module.ts | 4 ++-- .../rule-node/flow/flow-rule-node-config.module.ts | 4 ++-- .../transformation/transformation-rule-node-config.module.ts | 5 ++--- 6 files changed, 12 insertions(+), 13 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/action-rule-node-config.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/action-rule-node-config.module.ts index 17307cb341..5e7b53c02b 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/action-rule-node-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/action-rule-node-config.module.ts @@ -14,9 +14,9 @@ /// limitations under the License. /// -import { NgModule, Type } from '@angular/core'; +import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { IRuleNodeConfigurationComponent, SharedModule } from '@shared/public-api'; +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'; diff --git a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/enrichment-rule-node-core.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/enrichment-rule-node-core.module.ts index 7d614ecd96..a20f387e34 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/enrichment/enrichment-rule-node-core.module.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/enrichment/enrichment-rule-node-core.module.ts @@ -14,9 +14,9 @@ /// limitations under the License. /// -import { NgModule, Type } from '@angular/core'; +import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { IRuleNodeConfigurationComponent, SharedModule } from '@shared/public-api'; +import { SharedModule } from '@shared/public-api'; import { CustomerAttributesConfigComponent } from './customer-attributes-config.component'; import { CommonRuleNodeConfigModule } from '../common/common-rule-node-config.module'; import { EntityDetailsConfigComponent } from './entity-details-config.component'; diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/external-rule-node-config.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/external/external-rule-node-config.module.ts index c3c8c2ca2c..ef550d8460 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/external/external-rule-node-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/external-rule-node-config.module.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { NgModule, Type } from '@angular/core'; +import { NgModule } from '@angular/core'; import { SnsConfigComponent } from './sns-config.component'; import { SqsConfigComponent } from './sqs-config.component'; import { PubSubConfigComponent } from './pubsub-config.component'; @@ -27,7 +27,7 @@ import { SendEmailConfigComponent } from './send-email-config.component'; import { AzureIotHubConfigComponent } from './azure-iot-hub-config.component'; import { SendSmsConfigComponent } from './send-sms-config.component'; import { CommonModule } from '@angular/common'; -import { IRuleNodeConfigurationComponent, SharedModule } from '@shared/public-api'; +import { SharedModule } from '@shared/public-api'; import { HomeComponentsModule } from '@home/components/public-api'; import { CommonRuleNodeConfigModule } from '../common/common-rule-node-config.module'; import { SlackConfigComponent } from './slack-config.component'; diff --git a/ui-ngx/src/app/modules/home/components/rule-node/filter/filter-rule-node-config.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/filter/filter-rule-node-config.module.ts index bb3636811b..2b26991a16 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/filter/filter-rule-node-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/filter/filter-rule-node-config.module.ts @@ -14,9 +14,9 @@ /// limitations under the License. /// -import { NgModule, Type } from '@angular/core'; +import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { IRuleNodeConfigurationComponent, SharedModule } from '@shared/public-api'; +import { SharedModule } from '@shared/public-api'; import { CheckMessageConfigComponent } from './check-message-config.component'; import { CheckRelationConfigComponent } from './check-relation-config.component'; import { GpsGeoFilterConfigComponent } from './gps-geo-filter-config.component'; diff --git a/ui-ngx/src/app/modules/home/components/rule-node/flow/flow-rule-node-config.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/flow/flow-rule-node-config.module.ts index 8a703ca9d2..95668cf19e 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/flow/flow-rule-node-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/flow/flow-rule-node-config.module.ts @@ -14,9 +14,9 @@ /// limitations under the License. /// -import { NgModule, Type } from '@angular/core'; +import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { IRuleNodeConfigurationComponent, SharedModule } from '@shared/public-api'; +import { SharedModule } from '@shared/public-api'; import { RuleChainInputComponent } from './rule-chain-input.component'; import { RuleChainOutputComponent } from './rule-chain-output.component'; diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transformation/transformation-rule-node-config.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/transformation/transformation-rule-node-config.module.ts index 76ba7934d6..5104d3ac81 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/transformation/transformation-rule-node-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/transformation/transformation-rule-node-config.module.ts @@ -14,9 +14,9 @@ /// limitations under the License. /// -import { NgModule, Type } from '@angular/core'; +import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { IRuleNodeConfigurationComponent, SharedModule } from '@shared/public-api'; +import { SharedModule } from '@shared/public-api'; import { ChangeOriginatorConfigComponent } from './change-originator-config.component'; import { CommonRuleNodeConfigModule } from '../common/common-rule-node-config.module'; import { TransformScriptConfigComponent } from './script-config.component'; @@ -26,7 +26,6 @@ import { RenameKeysConfigComponent } from './rename-keys-config.component'; import { NodeJsonPathConfigComponent } from './node-json-path-config.component'; import { DeleteKeysConfigComponent } from './delete-keys-config.component'; import { DeduplicationConfigComponent } from './deduplication-config.component'; -import { ScriptConfigComponent } from '@home/components/rule-node/filter/script-config.component'; @NgModule({ declarations: [ From 3dfcc318e070675df6eb95e2470c4b76fe132516 Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Wed, 22 Jan 2025 16:21:47 +0200 Subject: [PATCH 092/108] Save time series strategies: add information about strategies in node description; refactor node description --- .../engine/telemetry/TbMsgTimeseriesNode.java | 55 +++++++++++++++---- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java index 232b8a3d36..31deddf63b 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java @@ -57,18 +57,49 @@ import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_RE type = ComponentType.ACTION, name = "save time series", configClazz = TbMsgTimeseriesNodeConfiguration.class, - nodeDescription = "Saves time series data", - nodeDetails = "Saves time series telemetry data based on configurable TTL parameter. Expects messages with 'POST_TELEMETRY_REQUEST' message type. " + - "Timestamp in milliseconds will be taken from metadata.ts, otherwise 'now' message timestamp will be applied. " + - "Allows stopping updating values for incoming keys in the latest ts_kv table if 'skipLatestPersistence' is set to true.\n " + - "
    " + - "Enable 'useServerTs' param to use the timestamp of the message processing instead of the timestamp from the message. " + - "Useful for all sorts of sequential processing if you merge messages from multiple sources (devices, assets, etc).\n" + - "
    " + - "In the case of sequential processing, the platform guarantees that the messages are processed in the order of their submission to the queue. " + - "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.", + nodeDescription = """ + Saves time series data with a configurable TTL and according to configured persistence strategies. + """, + nodeDetails = """ + Node performs three actions: +
      +
    • Time series: save time series data to a ts_kv table in a DB.
    • +
    • Latest values: save time series data to a ts_kv_latest table in a DB.
    • +
    • WebSockets: notify WebSockets subscriptions about time series data updates.
    • +
    + + For each action, three persistence strategies are available: +
      +
    • On every message: perform the action for every message.
    • +
    • Deduplicate: perform the action only for the first message from a particular originator within a configurable interval.
    • +
    • Skip: never perform the action.
    • +
    + + Persistence strategies are configured using persistence settings, which support two modes: +
      +
    • Basic +
        +
      • On every message: applies the "On every message" strategy to all actions.
      • +
      • Deduplicate: applies the "Deduplicate" strategy (with a specified interval) to all actions.
      • +
      • WebSockets only: applies the "Skip" strategy to Time series and Latest values, and the "On every message" strategy to WebSockets.
      • +
      +
    • +
    • Advanced: configure each action’s strategy independently.
    • +
    + + By default, the timestamp is taken from metadata.ts. You can enable + Use server timestamp to always use the current server time instead. This is particularly + useful in sequential processing scenarios where messages may arrive with out-of-order timestamps from + multiple sources. Note that the DB layer may ignore older records for attributes and latest values, + so enabling Use server timestamp can ensure correct ordering. +

    + The TTL is taken first from metadata.TTL. If absent, the node configuration’s default + TTL is used. If neither is set, the tenant profile default applies. +

    + This node expects messages of type POST_TELEMETRY_REQUEST. +

    + Output connections: Success, Failure. + """, uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeTimeseriesConfig", icon = "file_upload", From e57746167dcdd582e4097e11fc8b6a9d06bbb0b0 Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Thu, 23 Jan 2025 10:55:54 +0200 Subject: [PATCH 093/108] Update locale.constant-en_US.json --- ui-ngx/src/assets/locale/locale.constant-en_US.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index f19336d6ad..b5903a8fb6 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -4907,7 +4907,7 @@ "alarm-severity-pattern-hint": "Use ${metadataKey} for value from metadata, $[messageKey] for value from message body. Alarm severity should be system (CRITICAL, MAJOR etc.)", "output-node-name-hint": "The 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.", "use-server-ts": "Use server timestamp", - "use-server-ts-hint": "Rule node will use the timestamp of message processing instead of the timestamp from the message. Useful for all sorts of sequential processing if you merge messages from multiple sources (devices, assets, etc).", + "use-server-ts-hint": "Use the server’s current timestamp for time series data that lacks an explicit timestamp. This helps maintain proper ordering when processing messages from multiple sources or when messages arrive out of sequence.", "kv-map-pattern-hint": "All input fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", "kv-map-single-pattern-hint": "Input field support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.", "shared-scope": "Shared scope", @@ -5072,7 +5072,7 @@ "advanced-mode": "Advanced", "save-time-series": { "persistence-settings": "Persistence settings", - "persistence-settings-hint": "Persistence settings hint", + "persistence-settings-hint": "Define how and when time series data is saved. In Basic mode, apply a single persistence strategy to all actions or enable only WebSockets updates. Advanced mode allows you to configure individual persistence strategies for each action.", "strategy": "Strategy", "deduplication-interval": "Deduplication interval", "deduplication-interval-required": "Deduplication interval is required", From 80593f4b52d09703e397bb20799d9501baed1f60 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 23 Jan 2025 14:05:41 +0200 Subject: [PATCH 094/108] UI: Fixed validation and style in Save ts rule node config --- ...ced-persistence-setting-row.component.html | 1 + .../action/timeseries-config.component.html | 15 ++++++------- .../action/timeseries-config.component.ts | 8 +++---- .../action/timeseries-config.models.ts | 2 +- .../common/time-unit-input.component.html | 2 +- .../common/time-unit-input.component.ts | 21 ++++++++++++++----- 6 files changed, 31 insertions(+), 18 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/advanced-persistence-setting-row.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/advanced-persistence-setting-row.component.html index 038fe41bb2..2bb51ad504 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/advanced-persistence-setting-row.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/advanced-persistence-setting-row.component.html @@ -32,6 +32,7 @@ requiredText="{{ 'rule-node-config.save-time-series.deduplication-interval-required' | translate }}" minErrorText="{{ 'rule-node-config.save-time-series.deduplication-interval-min-max-range' | translate }}" maxErrorText="{{ 'rule-node-config.save-time-series.deduplication-interval-min-max-range' | translate }}" + [minTime]="1" [maxTime]="maxDeduplicateTime" formControlName="deduplicationIntervalSecs"> diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.html index 47655c1285..eb2811c8f8 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.html @@ -45,6 +45,7 @@ minErrorText="{{ 'rule-node-config.save-time-series.deduplication-interval-min-max-range' | translate }}" maxErrorText="{{ 'rule-node-config.save-time-series.deduplication-interval-min-max-range' | translate }}" [maxTime]="maxDeduplicateTime" + [minTime]="1" formControlName="deduplicationIntervalSecs"> } @@ -56,12 +57,18 @@ > }
    -
    +
    rule-node-config.advanced-settings +
    + + {{ 'rule-node-config.use-server-ts' | translate }} + +
    -
    - - {{ 'rule-node-config.use-server-ts' | translate }} - -
    diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.ts index a834d0d43f..4a061d3193 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.ts @@ -60,16 +60,16 @@ export class TimeseriesConfigComponent extends RuleNodeConfigurationComponent { if (config?.persistenceSettings) { const isAdvanced = config?.persistenceSettings?.type === PersistenceType.ADVANCED; persistenceSettings = { - ...config.persistenceSettings, - isAdvanced: isAdvanced, type: isAdvanced ? PersistenceType.ON_EVERY_MESSAGE : config.persistenceSettings.type, + isAdvanced: isAdvanced, + deduplicationIntervalSecs: config.persistenceSettings?.deduplicationIntervalSecs ?? 60, advanced: isAdvanced ? config.persistenceSettings : defaultAdvancedPersistenceStrategy } } else { persistenceSettings = { type: PersistenceType.ON_EVERY_MESSAGE, isAdvanced: false, - deduplicationIntervalSecs: 10, + deduplicationIntervalSecs: 60, advanced: defaultAdvancedPersistenceStrategy }; } @@ -104,7 +104,7 @@ export class TimeseriesConfigComponent extends RuleNodeConfigurationComponent { isAdvanced: [config?.persistenceSettings?.isAdvanced ?? false], type: [config?.persistenceSettings?.type ?? PersistenceType.ON_EVERY_MESSAGE], deduplicationIntervalSecs: [ - {value: config?.persistenceSettings?.deduplicationIntervalSecs ?? 10, disabled: true}, + {value: config?.persistenceSettings?.deduplicationIntervalSecs ?? 60, disabled: true}, [Validators.required, Validators.max(maxDeduplicateTimeSecs)] ], advanced: [{value: null, disabled: true}] diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.models.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.models.ts index 241787d511..f70e8548b1 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.models.ts @@ -16,7 +16,7 @@ import { DAY, SECOND } from '@shared/models/time/time.models'; -export const maxDeduplicateTimeSecs = DAY / SECOND + 1; +export const maxDeduplicateTimeSecs = DAY / SECOND; export interface TimeseriesNodeConfiguration { persistenceSettings: PersistenceSettings; diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.html index 1fd0a60159..9f65a2d9fc 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.html @@ -33,7 +33,7 @@ {{ maxErrorText }} - + rule-node-config.units @for (timeUnit of timeUnits; track timeUnit) { diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.ts index 3dc0c00124..08d501477c 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.ts @@ -22,7 +22,8 @@ import { NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, - Validator, Validators + Validator, + Validators } from '@angular/forms'; import { TimeUnit, timeUnitTranslations } from '../rule-node-config.models'; import { isDefinedAndNotNull, isNumeric } from '@core/utils'; @@ -60,6 +61,10 @@ export class TimeUnitInputComponent implements ControlValueAccessor, Validator, @Input() requiredText: string; + @Input() + @coerceNumber() + minTime = 0; + @Input() minErrorText: string; @@ -75,7 +80,7 @@ export class TimeUnitInputComponent implements ControlValueAccessor, Validator, timeUnitTranslations = timeUnitTranslations; timeInputForm = this.fb.group({ - time: [0, Validators.min(0)], + time: [0], timeUnit: [TimeUnit.SECONDS] }); @@ -97,7 +102,7 @@ export class TimeUnitInputComponent implements ControlValueAccessor, Validator, ngOnInit() { if(this.required || this.maxTime) { const timeControl = this.timeInputForm.get('time'); - const validators = []; + const validators = [Validators.pattern(/^\d*$/)]; if (this.required) { validators.push(Validators.required); } @@ -106,6 +111,9 @@ export class TimeUnitInputComponent implements ControlValueAccessor, Validator, Validators.max(Math.floor(this.maxTime / this.timeIntervalsInSec.get(this.timeInputForm.get('timeUnit').value)))(control) ); } + if (isDefinedAndNotNull(this.minTime)) { + validators.push(Validators.min(this.minTime)); + } timeControl.setValidators(validators); timeControl.updateValueAndValidity({ emitEvent: false }); @@ -137,6 +145,9 @@ export class TimeUnitInputComponent implements ControlValueAccessor, Validator, this.timeInputForm.disable({emitEvent: false}); } else { this.timeInputForm.enable({emitEvent: false}); + if(this.timeInputForm.invalid) { + setTimeout(() => this.updatedModel(this.timeInputForm.value, true)) + } } } @@ -161,9 +172,9 @@ export class TimeUnitInputComponent implements ControlValueAccessor, Validator, }; } - private updatedModel(value: Partial) { + private updatedModel(value: Partial, forceUpdated = false) { const time = value.time * this.timeIntervalsInSec.get(value.timeUnit); - if (this.modelValue !== time) { + if (this.modelValue !== time || forceUpdated) { this.modelValue = time; this.propagateChange(time); } From e8dbe562191da733e29530dc1f452a41f4535776 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 23 Jan 2025 17:01:21 +0200 Subject: [PATCH 095/108] UI: Fixed advanced settings style in Save ts rule node config --- .../rule-node/action/timeseries-config.component.html | 3 ++- .../rule-node/common/time-unit-input.component.html | 4 ++-- .../components/rule-node/common/time-unit-input.component.ts | 4 ++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.html index eb2811c8f8..e803d7443d 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.html @@ -57,7 +57,7 @@ > }
    -
    +
    rule-node-config.advanced-settings @@ -71,6 +71,7 @@
    - + {{ requiredText }} @@ -33,7 +33,7 @@ {{ maxErrorText }} - + rule-node-config.units @for (timeUnit of timeUnits; track timeUnit) { diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.ts index 08d501477c..11358ae6eb 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/time-unit-input.component.ts @@ -30,6 +30,7 @@ import { isDefinedAndNotNull, isNumeric } from '@core/utils'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { coerceBoolean, coerceNumber } from '@shared/decorators/coercion'; import { DAY, HOUR, MINUTE, SECOND } from '@shared/models/time/time.models'; +import { SubscriptSizing } from '@angular/material/form-field'; interface TimeUnitInputModel { time: number; @@ -75,6 +76,9 @@ export class TimeUnitInputComponent implements ControlValueAccessor, Validator, @Input() maxErrorText: string; + @Input() + subscriptSizing: SubscriptSizing = 'fixed'; + timeUnits = Object.values(TimeUnit).filter(item => item !== TimeUnit.MILLISECONDS) as TimeUnit[]; timeUnitTranslations = timeUnitTranslations; From a0c5f72b0f9e107efc32e82ad787cf4f9264f110 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 24 Jan 2025 15:37:48 +0200 Subject: [PATCH 096/108] UI: Fixed error when used 'Update device attribute' widgets --- .../data/json/system/widget_types/update_device_attribute.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/data/json/system/widget_types/update_device_attribute.json b/application/src/main/data/json/system/widget_types/update_device_attribute.json index 92111daf54..763ba93bc9 100644 --- a/application/src/main/data/json/system/widget_types/update_device_attribute.json +++ b/application/src/main/data/json/system/widget_types/update_device_attribute.json @@ -9,7 +9,7 @@ "sizeX": 4, "sizeY": 2, "resources": [], - "templateHtml": "
    \n
    \n {{title}}\n
    \n
    \n
    \n \n
    \n
    \n
    \n {{ error }}\n
    \n
    ", + "templateHtml": "
    \n
    \n {{title}}\n
    \n
    \n
    \n \n
    \n
    \n
    \n {{ error }}\n
    \n
    ", "templateCss": ".tb-rpc-button {\n width: 100%;\n height: 100%;\n}\n\n.tb-rpc-button .title-container {\n font-weight: 500;\n white-space: nowrap;\n margin: 10px 0;\n}\n\n.tb-rpc-button .button-container div{\n min-width: 80%\n}\n\n.tb-rpc-button .button-container .mat-mdc-button{\n width: 100%;\n margin: 0;\n}\n\n.tb-rpc-button .error-container {\n position: absolute;\n top: 2%;\n right: 0;\n left: 0;\n z-index: 4;\n height: 14px;\n}\n\n.tb-rpc-button .error-container .button-error {\n color: #ff3315;\n white-space: nowrap;\n}", "controllerScript": "self.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges();\n });\n};\n\nfunction init() {\n self.ctx.$scope.buttonLable = self.ctx.settings.buttonText;\n self.ctx.$scope.showTitle = self.ctx.settings.title &&\n self.ctx.settings.title.length ? true : false;\n self.ctx.$scope.title = self.ctx.settings.title;\n self.ctx.$scope.styleButton = self.ctx.settings.styleButton;\n let entityAttributeType = self.ctx.settings.entityAttributeType;\n let entityParameters = JSON.parse(self.ctx.settings.entityParameters);\n\n if (self.ctx.settings.styleButton.isPrimary ===\n false) {\n self.ctx.$scope.customStyle = {\n 'background-color': self.ctx.$scope.styleButton\n .bgColor,\n 'color': self.ctx.$scope.styleButton.textColor\n };\n }\n\n let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n\n self.ctx.$scope.sendUpdate = function() {\n let attributes = [];\n for (let key in entityParameters) {\n attributes.push({\n \"key\": key,\n \"value\": entityParameters[key]\n });\n }\n \n let entityId = {\n entityType: \"DEVICE\",\n id: self.ctx.defaultSubscription.targetDeviceId\n };\n attributeService.saveEntityAttributes(entityId,\n entityAttributeType, attributes).subscribe(\n function success() {\n self.ctx.$scope.error = \"\";\n self.ctx.detectChanges();\n },\n function fail(rejection) {\n if (self.ctx.settings.showError) {\n self.ctx.$scope.error =\n rejection.status + \": \" +\n rejection.statusText;\n self.ctx.detectChanges();\n }\n }\n\n );\n };\n}\n", "settingsSchema": "", From 8bbdf89a3dcf6e81f7f9cfca59329e9733c08a23 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 24 Jan 2025 16:29:07 +0200 Subject: [PATCH 097/108] UI: Scada energy widget bundle --- .../3-phase-voltage-relay-hp.svg | 548 +++++++++++ .../system/scada_symbols/apartments-hp.svg | 344 +++++++ .../json/system/scada_symbols/battery-hp.svg | 473 ++++++++++ .../scada_symbols/bottom-light-bulb-hp.svg | 346 +++++++ .../system/scada_symbols/consumers-hp.svg | 346 +++++++ .../scada_symbols/curcuit-breaker-hp.svg | 444 +++++++++ .../electrical-distribution-board-hp.svg | 332 +++++++ .../system/scada_symbols/energy-meter-hp.svg | 482 ++++++++++ .../four-rate-energy-meter-hp.svg | 876 ++++++++++++++++++ .../scada_symbols/fuel-generator-hp.svg | 359 +++++++ .../scada_symbols/high-voltage-tower-hp.svg | 297 ++++++ .../horizontal-curcuit-breaker-hp.svg | 439 +++++++++ ...horizontal-energy-system-controller-hp.svg | 378 ++++++++ .../json/system/scada_symbols/house-hp.svg | 349 +++++++ .../industrial-fuel-generator-hp.svg | 358 +++++++ .../json/system/scada_symbols/inverter-hp.svg | 578 ++++++++++++ .../scada_symbols/large-inverter-hp.svg | 578 ++++++++++++ .../scada_symbols/low-voltage-tower-hp.svg | 278 ++++++ .../low-voltage-transformer-tower-hp.svg | 334 +++++++ .../system/scada_symbols/manufacture-hp.svg | 344 +++++++ .../system/scada_symbols/power-socket-hp.svg | 371 ++++++++ .../scada_symbols/power-transformer-hp.svg | 359 +++++++ .../scada_symbols/single-key-switch-hp.svg | 373 ++++++++ .../small-power-transformer-hp.svg | 359 +++++++ .../system/scada_symbols/solar-panel-hp.svg | 353 +++++++ .../scada_symbols/stand-solar-panel-hp.svg | 357 +++++++ .../three-rate-energy-meter-hp.svg | 747 +++++++++++++++ .../scada_symbols/top-light-bulb-hp.svg | 346 +++++++ .../scada_symbols/two-key-switch-hp.svg | 489 ++++++++++ .../two-rate-energy-meter-hp.svg | 618 ++++++++++++ .../vertical-energy-system-controller-hp.svg | 378 ++++++++ .../system/scada_symbols/voltage-relay-hp.svg | 439 +++++++++ .../scada_symbols/voltage-stabilizer-hp.svg | 584 ++++++++++++ .../scada_symbols/wind-turbine-cluster-hp.svg | 370 ++++++++ .../system/scada_symbols/wind-turbine-hp.svg | 360 +++++++ ...eneral_high_performance_scada_symbols.json | 6 +- .../high_performance_scada_energy_system.json | 48 + .../assets/locale/locale.constant-en_US.json | 60 ++ 38 files changed, 15099 insertions(+), 1 deletion(-) create mode 100644 application/src/main/data/json/system/scada_symbols/3-phase-voltage-relay-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/apartments-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/battery-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/bottom-light-bulb-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/consumers-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/curcuit-breaker-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/electrical-distribution-board-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/energy-meter-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/four-rate-energy-meter-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/fuel-generator-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/high-voltage-tower-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/horizontal-curcuit-breaker-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/horizontal-energy-system-controller-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/house-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/industrial-fuel-generator-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/inverter-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/large-inverter-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/low-voltage-tower-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/low-voltage-transformer-tower-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/manufacture-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/power-socket-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/power-transformer-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/single-key-switch-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/small-power-transformer-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/solar-panel-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/stand-solar-panel-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/three-rate-energy-meter-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/top-light-bulb-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/two-key-switch-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/two-rate-energy-meter-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/vertical-energy-system-controller-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/voltage-relay-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/voltage-stabilizer-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/wind-turbine-cluster-hp.svg create mode 100644 application/src/main/data/json/system/scada_symbols/wind-turbine-hp.svg create mode 100644 application/src/main/data/json/system/widget_bundles/high_performance_scada_energy_system.json diff --git a/application/src/main/data/json/system/scada_symbols/3-phase-voltage-relay-hp.svg b/application/src/main/data/json/system/scada_symbols/3-phase-voltage-relay-hp.svg new file mode 100644 index 0000000000..f6d881b172 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/3-phase-voltage-relay-hp.svg @@ -0,0 +1,548 @@ +{ + "title": "HP 3 phase voltage relay", + "description": "Three phase voltage relay with various states and inications.", + "searchTags": [ + "energy", + "power", + "protection" + ], + "widgetSizeX": 2, + "widgetSizeY": 2, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "firstPhaseIndicator", + "stateRenderFunction": "if (ctx.values.running) {\n element.stroke(ctx.properties.phaseIndicatorColor);\n if (!ctx.values.firstPhaseVoltage) {\n element.fill('#fff');\n } else {\n element.fill(ctx.properties.phaseIndicatorColor);\n }\n} else {\n element.stroke('#000');\n element.fill(ctx.properties.stoppedColor);\n}", + "actions": null + }, + { + "tag": "firstPhaseValue", + "stateRenderFunction": "if (ctx.values.running) {\n element.show();\n ctx.api.font(element, ctx.properties.currentVoltageFont, ctx.properties.currentVoltageColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.firstPhaseVoltage, 0, null, 0));\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "secondPhaseIndicator", + "stateRenderFunction": "if (ctx.values.running) {\n element.stroke(ctx.properties.phaseIndicatorColor);\n if (!ctx.values.secondPhaseVoltage) {\n element.fill('#fff');\n } else {\n element.fill(ctx.properties.phaseIndicatorColor);\n }\n} else {\n element.stroke('#000');\n element.fill(ctx.properties.stoppedColor);\n}", + "actions": null + }, + { + "tag": "secondPhaseValue", + "stateRenderFunction": "if (ctx.values.running) {\n element.show();\n ctx.api.font(element, ctx.properties.currentVoltageFont, ctx.properties.currentVoltageColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.secondPhaseVoltage, 0, null, 0));\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "thirdPhaseIndicator", + "stateRenderFunction": "if (ctx.values.running) {\n element.stroke(ctx.properties.phaseIndicatorColor);\n if (!ctx.values.thirdPhaseVoltage) {\n element.fill('#fff');\n } else {\n element.fill(ctx.properties.phaseIndicatorColor);\n }\n} else {\n element.stroke('#000');\n element.fill(ctx.properties.stoppedColor);\n}", + "actions": null + }, + { + "tag": "thirdPhaseValue", + "stateRenderFunction": "if (ctx.values.running) {\n element.show();\n ctx.api.font(element, ctx.properties.currentVoltageFont, ctx.properties.currentVoltageColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.thirdPhaseVoltage, 0, null, 0));\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "units", + "stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.properties.units);\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "value-box", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.valueBoxBackground;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "firstPhaseVoltage", + "name": "{i18n:scada.symbol.first-phase-voltage}", + "hint": "{i18n:scada.symbol.phase-voltage-hint}", + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "GET_TIME_SERIES", + "defaultValue": null, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "firstPhaseVoltage" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "secondPhaseVoltage", + "name": "{i18n:scada.symbol.second-phase-voltage}", + "hint": "{i18n:scada.symbol.phase-voltage-hint}", + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "GET_TIME_SERIES", + "defaultValue": null, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "secondPhaseVoltage" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "thirdPhaseVoltage", + "name": "{i18n:scada.symbol.third-phase-voltage}", + "hint": "{i18n:scada.symbol.phase-voltage-hint}", + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "GET_TIME_SERIES", + "defaultValue": null, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "thirdPhaseVoltage" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "subLabel": "{i18n:scada.symbol.stopped}", + "disabled": false, + "visible": true + }, + { + "id": "valueBoxBackground", + "name": "{i18n:scada.symbol.value-box-background}", + "type": "color", + "default": "#DEDEDE", + "disabled": false, + "visible": true + }, + { + "id": "phaseIndicatorColor", + "name": "{i18n:scada.symbol.phase-indicator-color}", + "type": "color", + "default": "#198038", + "disabled": false, + "visible": true + }, + { + "id": "currentVoltageFont", + "name": "{i18n:scada.symbol.current-voltage-color}", + "type": "font", + "default": { + "size": 32, + "sizeUnit": "px", + "family": "Roboto", + "weight": "400", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "currentVoltageColor", + "name": "{i18n:scada.symbol.current-voltage-color}", + "type": "color", + "default": "#002878", + "disabled": false, + "visible": true + }, + { + "id": "showUnits", + "name": "{i18n:scada.symbol.units}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "units", + "name": "{i18n:scada.symbol.units}", + "type": "units", + "default": "V", + "disabled": false, + "visible": true + }, + { + "id": "unitsFont", + "name": "{i18n:scada.symbol.units}", + "type": "font", + "default": { + "size": 24, + "sizeUnit": "px", + "family": "Roboto", + "weight": "500", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "unitsColor", + "name": "{i18n:scada.symbol.units}", + "type": "color", + "default": "#000", + "disabled": false, + "visible": true + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + } + ] +} +220220220v + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/apartments-hp.svg b/application/src/main/data/json/system/scada_symbols/apartments-hp.svg new file mode 100644 index 0000000000..9e2c965f79 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/apartments-hp.svg @@ -0,0 +1,344 @@ +{ + "title": "HP Apartments", + "description": "Apartments with various states.", + "searchTags": [ + "power", + "energy", + "consumer" + ], + "widgetSizeX": 2, + "widgetSizeY": 2, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "required": null, + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "required": null, + "subLabel": "{i18n:scada.symbol.stopped}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "required": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "required": null, + "subLabel": "{i18n:scada.symbol.critical}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + } + ] +} + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/battery-hp.svg b/application/src/main/data/json/system/scada_symbols/battery-hp.svg new file mode 100644 index 0000000000..8b63a9ab29 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/battery-hp.svg @@ -0,0 +1,473 @@ +{ + "title": "HP Battery", + "description": "Battery with various states and scalable quantity.", + "searchTags": [ + "energy", + "power", + "rechargeable", + "storage", + "lithium-ion", + "ev" + ], + "widgetSizeX": 3, + "widgetSizeY": 1, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "indicator", + "stateRenderFunction": "if (ctx.values.running) {\n element.fill(ctx.properties.runningIndicatorColor);\n} else {\n element.fill(ctx.properties.stoppedIndicatorColor);\n}", + "actions": null + }, + { + "tag": "label", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.labelFont, ctx.properties.labelColor);\nctx.api.text(element, ctx.properties.label);", + "actions": null + }, + { + "tag": "left-bottom-connector", + "stateRenderFunction": "if (!ctx.properties.leftBottomConnector) {\n element.hide();\n}", + "actions": null + }, + { + "tag": "left-connector", + "stateRenderFunction": "if (!ctx.properties.leftConnector) {\n element.hide();\n}", + "actions": null + }, + { + "tag": "left-top-connector", + "stateRenderFunction": "if (!ctx.properties.leftTopConnector) {\n element.hide();\n}", + "actions": null + }, + { + "tag": "right-bottom-connector", + "stateRenderFunction": "if (!ctx.properties.rightBottomConnector) {\n element.hide();\n}", + "actions": null + }, + { + "tag": "right-connector", + "stateRenderFunction": "if (!ctx.properties.rightConnector) {\n element.hide();\n}", + "actions": null + }, + { + "tag": "right-top-connector", + "stateRenderFunction": "if (!ctx.properties.rightTopConnector) {\n element.hide();\n}", + "actions": null + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "subLabel": "{i18n:scada.symbol.stopped}", + "disabled": false, + "visible": true + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + }, + { + "id": "runningIndicatorColor", + "name": "{i18n:scada.symbol.indicator-colors}", + "type": "color", + "default": "#198038", + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "stoppedIndicatorColor", + "name": "{i18n:scada.symbol.indicator-colors}", + "type": "color", + "default": "#DEDEDE", + "subLabel": "{i18n:scada.symbol.stopped}", + "disabled": false, + "visible": true + }, + { + "id": "label", + "name": "{i18n:scada.symbol.label}", + "type": "text", + "default": "ON", + "fieldClass": "medium-width", + "disabled": false, + "visible": true + }, + { + "id": "labelFont", + "name": "{i18n:scada.symbol.label}", + "type": "font", + "default": { + "size": 30, + "sizeUnit": "px", + "family": "Roboto", + "weight": "400", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "labelColor", + "name": "{i18n:scada.symbol.label}", + "type": "color", + "default": "#1A1A1A", + "disabled": false, + "visible": true + }, + { + "id": "leftConnector", + "name": "{i18n:scada.symbol.left-connector}", + "group": "{i18n:scada.symbol.connectors-positions}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, + { + "id": "leftTopConnector", + "name": "{i18n:scada.symbol.left-top-connector}", + "group": "{i18n:scada.symbol.connectors-positions}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, + { + "id": "leftBottomConnector", + "name": "{i18n:scada.symbol.left-bottom-connector}", + "group": "{i18n:scada.symbol.connectors-positions}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, + { + "id": "rightConnector", + "name": "{i18n:scada.symbol.right-connector}", + "group": "{i18n:scada.symbol.connectors-positions}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, + { + "id": "rightTopConnector", + "name": "{i18n:scada.symbol.right-top-connector}", + "group": "{i18n:scada.symbol.connectors-positions}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, + { + "id": "rightBottomConnector", + "name": "{i18n:scada.symbol.right-bottom-connector}", + "group": "{i18n:scada.symbol.connectors-positions}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + } + ] +} + + + + + + + + + ON + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/bottom-light-bulb-hp.svg b/application/src/main/data/json/system/scada_symbols/bottom-light-bulb-hp.svg new file mode 100644 index 0000000000..4f0603b904 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/bottom-light-bulb-hp.svg @@ -0,0 +1,346 @@ +{ + "title": "HP Bottom light bulb", + "description": "Bottom light bulb with various states.", + "searchTags": [ + "energy" + ], + "widgetSizeX": 1, + "widgetSizeY": 1, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "required": null, + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "required": null, + "subLabel": "{i18n:scada.symbol.stopped}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "required": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "required": null, + "subLabel": "{i18n:scada.symbol.critical}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + } + ] +} + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/consumers-hp.svg b/application/src/main/data/json/system/scada_symbols/consumers-hp.svg new file mode 100644 index 0000000000..e4005d7ae2 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/consumers-hp.svg @@ -0,0 +1,346 @@ +{ + "title": "HP Consumers", + "description": "Consumers with various states.", + "searchTags": [ + "power", + "energy" + ], + "widgetSizeX": 3, + "widgetSizeY": 2, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "required": null, + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "required": null, + "subLabel": "{i18n:scada.symbol.stopped}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "required": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "required": null, + "subLabel": "{i18n:scada.symbol.critical}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + } + ] +} + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/curcuit-breaker-hp.svg b/application/src/main/data/json/system/scada_symbols/curcuit-breaker-hp.svg new file mode 100644 index 0000000000..12f8b4944f --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/curcuit-breaker-hp.svg @@ -0,0 +1,444 @@ +{ + "title": "HP Circuit breaker", + "description": "Circuit breaker with various states.", + "searchTags": [ + "energy", + "power", + "ev", + "switch" + ], + "widgetSizeX": 1, + "widgetSizeY": 2, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "element.attr({fill: ctx.properties.backgroundColor});", + "actions": null + }, + { + "tag": "breaker", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "var initial = ctx.values.initialState;\nvar action = initial ? 'offUpdateState' : 'onUpdateState';\n\nctx.api.callAction(event, action, undefined, {\n next: () => {\n ctx.api.setValue('initialState', !initial);\n }\n});" + } + } + }, + { + "tag": "breaker-trigger", + "stateRenderFunction": "element.fill(ctx.properties.disabledColor);\nif (ctx.values.initialState) {\n element.transform({translateY: 0});\n} else {\n element.transform({translateY: 160});\n}", + "actions": null + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "label", + "stateRenderFunction": "if (ctx.properties.label) {\n element.show();\n var label = ctx.values.initialState ? ctx.properties.onLabel : ctx.properties.offLabel;\n ctx.api.font(element, ctx.properties.labelFont, ctx.properties.labelColor);\n ctx.api.text(element, label);\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "label-box", + "stateRenderFunction": "var color = ctx.properties.disabledColor;\nif (ctx.values.initialState) {\n color = ctx.properties.enabledColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "initialState", + "name": "{i18n:scada.symbol.on-off-state}", + "hint": "{i18n:scada.symbol.on-off-state-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.on}", + "falseLabel": "{i18n:scada.symbol.off}", + "stateLabel": "", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "onUpdateState", + "name": "{i18n:scada.symbol.on-update-state}", + "hint": "{i18n:scada.symbol.on-update-state-hint}", + "group": null, + "type": "action", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": { + "action": "EXECUTE_RPC", + "executeRpc": { + "method": "setState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "setAttribute": { + "scope": "SERVER_SCOPE", + "key": "state" + }, + "putTimeSeries": { + "key": "state" + }, + "valueToData": { + "type": "CONSTANT", + "constantValue": true, + "valueToDataFunction": "/* Convert input boolean value to RPC parameters or attribute/time-series value */\nreturn value;" + } + }, + "defaultWidgetActionSettings": null + }, + { + "id": "offUpdateState", + "name": "{i18n:scada.symbol.off-update-state}", + "hint": "{i18n:scada.symbol.off-update-state-hint}", + "group": null, + "type": "action", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": { + "action": "EXECUTE_RPC", + "executeRpc": { + "method": "setState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "setAttribute": { + "key": "state", + "scope": "SERVER_SCOPE" + }, + "putTimeSeries": { + "key": "state" + }, + "valueToData": { + "type": "CONSTANT", + "constantValue": false, + "valueToDataFunction": "/* Convert input boolean value to RPC parameters or attribute/time-series value */\nreturn value;" + } + }, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + } + ], + "properties": [ + { + "id": "label", + "name": "{i18n:scada.symbol.label}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "onLabel", + "name": "{i18n:scada.symbol.label}", + "type": "text", + "default": "ON", + "disabled": false, + "visible": true + }, + { + "id": "offLabel", + "name": "{i18n:scada.symbol.label}", + "type": "text", + "default": "OFF", + "disabled": false, + "visible": true + }, + { + "id": "labelFont", + "name": "{i18n:scada.symbol.label}", + "type": "font", + "default": { + "size": 42, + "sizeUnit": "px", + "family": "Roboto", + "weight": "400", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "labelColor", + "name": "{i18n:scada.symbol.label}", + "type": "color", + "default": "#1A1A1A", + "disabled": false, + "visible": true + }, + { + "id": "backgroundColor", + "name": "{i18n:scada.symbol.background-color}", + "type": "color", + "default": "#FFFFFF", + "disabled": false, + "visible": true + }, + { + "id": "enabledColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "subLabel": "{i18n:scada.symbol.enabled}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "disabledColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "subLabel": "{i18n:scada.symbol.disabled}", + "disabled": false, + "visible": true + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + } + ] +} + + + + ON + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/electrical-distribution-board-hp.svg b/application/src/main/data/json/system/scada_symbols/electrical-distribution-board-hp.svg new file mode 100644 index 0000000000..0fcfe4dde0 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/electrical-distribution-board-hp.svg @@ -0,0 +1,332 @@ +{ + "title": "HP Electrical distribution board", + "description": "Electrical distribution board with various states.", + "searchTags": [ + "energy", + "power", + "fuse", + "panel" + ], + "widgetSizeX": 2, + "widgetSizeY": 3, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "element.attr({fill: ctx.properties.backgroundColor});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "icon", + "stateRenderFunction": "var showIcon = ctx.properties.showIcon;\nvar showLabel = ctx.properties.label;\nif (showIcon) {\n element.show();\n var icon = ctx.properties.icon;\n var iconSize = ctx.properties.iconSize;\n var iconColor = ctx.properties.iconColor;\n ctx.api.icon(element, icon, iconSize, iconColor, true);\n if (!showLabel) {\n element.transform({translateX: 200, translateY: 310});\n }\n} else {\n element.hide()\n}", + "actions": null + }, + { + "tag": "triangle", + "stateRenderFunction": "if (ctx.properties.showTriangle) {\n element.show();\n element.stroke(ctx.properties.triangleColor);\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "showIcon", + "name": "{i18n:scada.symbol.icon}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "iconSize", + "name": "{i18n:scada.symbol.icon}", + "type": "number", + "default": 44, + "fieldSuffix": "px", + "min": 0, + "disabled": false, + "visible": true + }, + { + "id": "icon", + "name": "{i18n:scada.symbol.icon}", + "type": "icon", + "default": "bolt", + "disabled": false, + "visible": true + }, + { + "id": "showTriangle", + "name": "{i18n:scada.symbol.triangle}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "triangleColor", + "name": "{i18n:scada.symbol.triangle}", + "type": "color", + "default": "#D12730", + "disabled": false, + "visible": true + }, + { + "id": "iconColor", + "name": "{i18n:scada.symbol.icon}", + "type": "color", + "default": "#000", + "disabled": false, + "visible": true + }, + { + "id": "backgroundColor", + "name": "{i18n:scada.symbol.background-color}", + "type": "color", + "default": "#FFFFFF", + "divider": false, + "disabled": false, + "visible": true + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + } + ] +} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/energy-meter-hp.svg b/application/src/main/data/json/system/scada_symbols/energy-meter-hp.svg new file mode 100644 index 0000000000..606eb1153d --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/energy-meter-hp.svg @@ -0,0 +1,482 @@ +{ + "title": "HP Energy meter", + "description": "Energy meter with various states.", + "searchTags": [ + "power", + "energy" + ], + "widgetSizeX": 2, + "widgetSizeY": 2, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "label", + "stateRenderFunction": "if (ctx.properties.showLabel) {\n element.show();\n ctx.api.font(element, ctx.properties.labelFont, ctx.properties.labelColor);\n ctx.api.text(element, ctx.properties.label);\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "units", + "stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.properties.units);\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "value", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.measured, 0, null, 0));", + "actions": null + }, + { + "tag": "value-box", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.valueBoxBackground;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "measured", + "name": "{i18n:scada.symbol.measured}", + "hint": "{i18n:scada.symbol.measured-hint}", + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "GET_TIME_SERIES", + "defaultValue": null, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "measured" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "subLabel": "{i18n:scada.symbol.stopped}", + "disabled": false, + "visible": true + }, + { + "id": "showLabel", + "name": "{i18n:scada.symbol.label}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "label", + "name": "{i18n:scada.symbol.label}", + "type": "text", + "default": "T1", + "fieldClass": "medium-width", + "disabled": false, + "visible": true + }, + { + "id": "labelFont", + "name": "{i18n:scada.symbol.label}", + "type": "font", + "default": { + "size": 36, + "sizeUnit": "px", + "family": "Roboto", + "weight": "400", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "labelColor", + "name": "{i18n:scada.symbol.label}", + "type": "color", + "default": "#000", + "disabled": false, + "visible": true + }, + { + "id": "showUnits", + "name": "{i18n:scada.symbol.units}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "units", + "name": "{i18n:scada.symbol.units}", + "type": "units", + "default": "kWh", + "disabled": false, + "visible": true + }, + { + "id": "unitsFont", + "name": "{i18n:scada.symbol.units}", + "type": "font", + "default": { + "size": 36, + "sizeUnit": "px", + "family": "Roboto", + "weight": "500", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "unitsColor", + "name": "{i18n:scada.symbol.units}", + "type": "color", + "default": "#000", + "disabled": false, + "visible": true + }, + { + "id": "valueFont", + "name": "{i18n:scada.symbol.value}", + "type": "font", + "default": { + "size": 48, + "sizeUnit": "px", + "family": "Roboto", + "weight": "500", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "valueColor", + "name": "{i18n:scada.symbol.value}", + "type": "color", + "default": "#002878", + "disabled": false, + "visible": true + }, + { + "id": "valueBoxBackground", + "name": "{i18n:scada.symbol.value-box-background}", + "type": "color", + "default": "#DEDEDE", + "disabled": false, + "visible": true + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + } + ] +} +000023kWhT1 + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/four-rate-energy-meter-hp.svg b/application/src/main/data/json/system/scada_symbols/four-rate-energy-meter-hp.svg new file mode 100644 index 0000000000..de4fb13836 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/four-rate-energy-meter-hp.svg @@ -0,0 +1,876 @@ +{ + "title": "HP Four-rate energy meter", + "description": "Four-rate energy meter with various states.", + "searchTags": [ + "power", + "energy" + ], + "widgetSizeX": 3, + "widgetSizeY": 2, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "export-label", + "stateRenderFunction": "if (ctx.properties.showExportLabel) {\n element.show();\n ctx.api.font(element, ctx.properties.exportLabelFont, ctx.properties.exportLabelColor);\n ctx.api.text(element, ctx.properties.exportLabel);\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "export-rate", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.exportValueFont, ctx.properties.exportValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.exportRate, 0, null, 0));", + "actions": null + }, + { + "tag": "night-label", + "stateRenderFunction": "if (ctx.properties.showNightLabel) {\n element.show();\n ctx.api.font(element, ctx.properties.nightLabelFont, ctx.properties.nightLabelColor);\n ctx.api.text(element, ctx.properties.nightLabel);\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "night-rate", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.nightValueFont, ctx.properties.nightValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.nightRate, 0, null, 0));", + "actions": null + }, + { + "tag": "off-peak-label", + "stateRenderFunction": "if (ctx.properties.showOffPeakLabel) {\n element.show();\n ctx.api.font(element, ctx.properties.offPeakLabelFont, ctx.properties.offPeakLabelColor);\n ctx.api.text(element, ctx.properties.offPeakLabel);\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "off-peak-rate", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.offPeakValueFont, ctx.properties.offPeakValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.offPeakRate, 0, null, 0));", + "actions": null + }, + { + "tag": "peak-label", + "stateRenderFunction": "if (ctx.properties.showPeakLabel) {\n element.show();\n ctx.api.font(element, ctx.properties.peakLabelFont, ctx.properties.peakLabelColor);\n ctx.api.text(element, ctx.properties.peakLabel);\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "peak-rate", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.peakValueFont, ctx.properties.peakValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.peakRate, 0, null, 0));", + "actions": null + }, + { + "tag": "units", + "stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.properties.units);\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "value-box-export", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.exportValueBoxBackground;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "value-box-night", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.nightValueBoxBackground;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "value-box-off-peak", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.offPeakValueBoxBackground;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "value-box-peak", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.peakValueBoxBackground;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "offPeakRate", + "name": "{i18n:scada.symbol.off-peak-rate}", + "hint": "{i18n:scada.symbol.measured-hint}", + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "GET_TIME_SERIES", + "defaultValue": null, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "offPeakRate" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "nightRate", + "name": "{i18n:scada.symbol.night-rate}", + "hint": "{i18n:scada.symbol.measured-hint}", + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "GET_TIME_SERIES", + "defaultValue": null, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "nightRate" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "peakRate", + "name": "{i18n:scada.symbol.peak-rate}", + "hint": "{i18n:scada.symbol.measured-hint}", + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "GET_TIME_SERIES", + "defaultValue": null, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "peakRate" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "exportRate", + "name": "{i18n:scada.symbol.export-rate}", + "hint": "{i18n:scada.symbol.measured-hint}", + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "GET_TIME_SERIES", + "defaultValue": null, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "exportRate" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "subLabel": "{i18n:scada.symbol.stopped}", + "disabled": false, + "visible": true + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + }, + { + "id": "showOffPeakLabel", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.off-peak-rate}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "offPeakLabel", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.off-peak-rate}", + "type": "text", + "default": "T1", + "fieldClass": "medium-width", + "disabled": false, + "visible": true + }, + { + "id": "offPeakLabelFont", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.off-peak-rate}", + "type": "font", + "default": { + "size": 36, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "offPeakLabelColor", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.off-peak-rate}", + "type": "color", + "default": "#000000", + "disabled": false, + "visible": true + }, + { + "id": "offPeakValueFont", + "name": "{i18n:scada.symbol.value}", + "group": "{i18n:scada.symbol.off-peak-rate}", + "type": "font", + "default": { + "size": 48, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "offPeakValueColor", + "name": "{i18n:scada.symbol.value}", + "group": "{i18n:scada.symbol.off-peak-rate}", + "type": "color", + "default": "#002878", + "disabled": false, + "visible": true + }, + { + "id": "offPeakValueBoxBackground", + "name": "{i18n:scada.symbol.value-box-background}", + "group": "{i18n:scada.symbol.off-peak-rate}", + "type": "color", + "default": "#DEDEDE", + "disabled": false, + "visible": true + }, + { + "id": "showNightLabel", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.night-rate}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "nightLabel", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.night-rate}", + "type": "text", + "default": "T2", + "fieldClass": "medium-width", + "disabled": false, + "visible": true + }, + { + "id": "nightLabelFont", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.night-rate}", + "type": "font", + "default": { + "size": 36, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "nightLabelColor", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.night-rate}", + "type": "color", + "default": "#000", + "disabled": false, + "visible": true + }, + { + "id": "nightValueFont", + "name": "{i18n:scada.symbol.value}", + "group": "{i18n:scada.symbol.night-rate}", + "type": "font", + "default": { + "size": 48, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "nightValueColor", + "name": "{i18n:scada.symbol.value}", + "group": "{i18n:scada.symbol.night-rate}", + "type": "color", + "default": "#002878", + "disabled": false, + "visible": true + }, + { + "id": "nightValueBoxBackground", + "name": "{i18n:scada.symbol.value-box-background}", + "group": "{i18n:scada.symbol.night-rate}", + "type": "color", + "default": "#DEDEDE", + "disabled": false, + "visible": true + }, + { + "id": "showPeakLabel", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.peak-rate}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "peakLabel", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.peak-rate}", + "type": "text", + "default": "T3", + "fieldClass": "medium-width", + "disabled": false, + "visible": true + }, + { + "id": "peakLabelFont", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.peak-rate}", + "type": "font", + "default": { + "size": 36, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "peakLabelColor", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.peak-rate}", + "type": "color", + "default": "#000", + "disabled": false, + "visible": true + }, + { + "id": "peakValueFont", + "name": "{i18n:scada.symbol.value}", + "group": "{i18n:scada.symbol.peak-rate}", + "type": "font", + "default": { + "size": 48, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "peakValueColor", + "name": "{i18n:scada.symbol.value}", + "group": "{i18n:scada.symbol.peak-rate}", + "type": "color", + "default": "#002878", + "disabled": false, + "visible": true + }, + { + "id": "peakValueBoxBackground", + "name": "{i18n:scada.symbol.value-box-background}", + "group": "{i18n:scada.symbol.peak-rate}", + "type": "color", + "default": "#DEDEDE", + "disabled": false, + "visible": true + }, + { + "id": "showExportLabel", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.export-rate}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "exportLabel", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.export-rate}", + "type": "text", + "default": "Export", + "fieldClass": "medium-width", + "disabled": false, + "visible": true + }, + { + "id": "exportLabelFont", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.export-rate}", + "type": "font", + "default": { + "size": 36, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "exportLabelColor", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.export-rate}", + "type": "color", + "default": "#000", + "disabled": false, + "visible": true + }, + { + "id": "exportValueFont", + "name": "{i18n:scada.symbol.value}", + "group": "{i18n:scada.symbol.export-rate}", + "type": "font", + "default": { + "size": 48, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "exportValueColor", + "name": "{i18n:scada.symbol.value}", + "group": "{i18n:scada.symbol.export-rate}", + "type": "color", + "default": "#002878", + "disabled": false, + "visible": true + }, + { + "id": "exportValueBoxBackground", + "name": "{i18n:scada.symbol.value-box-background}", + "group": "{i18n:scada.symbol.export-rate}", + "type": "color", + "default": "#DEDEDE", + "disabled": false, + "visible": true + }, + { + "id": "showUnits", + "name": "{i18n:scada.symbol.units}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "units", + "name": "{i18n:scada.symbol.units}", + "type": "units", + "default": "kWh", + "disabled": false, + "visible": true + }, + { + "id": "unitsFont", + "name": "{i18n:scada.symbol.units}", + "type": "font", + "default": { + "size": 36, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "unitsColor", + "name": "{i18n:scada.symbol.units}", + "type": "color", + "default": "#000", + "disabled": false, + "visible": true + } + ] +} +T1T2T3Export000223000223000223000223kWh + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/fuel-generator-hp.svg b/application/src/main/data/json/system/scada_symbols/fuel-generator-hp.svg new file mode 100644 index 0000000000..52a3c70d6e --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/fuel-generator-hp.svg @@ -0,0 +1,359 @@ +{ + "title": "HP Fuel generator", + "description": "Fuel generator with various states.", + "searchTags": [ + "power", + "energy", + "fuel", + "generation" + ], + "widgetSizeX": 1, + "widgetSizeY": 1, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "required": null, + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null, + "disabled": false, + "visible": true + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "required": null, + "subLabel": "{i18n:scada.symbol.stopped}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null, + "disabled": false, + "visible": true + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "required": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null, + "disabled": false, + "visible": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "required": null, + "subLabel": "{i18n:scada.symbol.critical}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null, + "disabled": false, + "visible": true + } + ] +} + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/high-voltage-tower-hp.svg b/application/src/main/data/json/system/scada_symbols/high-voltage-tower-hp.svg new file mode 100644 index 0000000000..3aef3ff948 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/high-voltage-tower-hp.svg @@ -0,0 +1,297 @@ +{ + "title": "HP High voltage tower", + "description": "High voltage tower with various states.", + "searchTags": [ + "power", + "energy" + ], + "widgetSizeX": 2, + "widgetSizeY": 4, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "element.attr({fill: ctx.properties.backgroundColor});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "backgroundColor", + "name": "{i18n:scada.symbol.background-color}", + "type": "color", + "default": "#999999", + "divider": false + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + } + ] +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/horizontal-curcuit-breaker-hp.svg b/application/src/main/data/json/system/scada_symbols/horizontal-curcuit-breaker-hp.svg new file mode 100644 index 0000000000..17fd1ba69e --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/horizontal-curcuit-breaker-hp.svg @@ -0,0 +1,439 @@ +{ + "title": "HP Horizontal circuit breaker", + "description": "Horizontal circuit breaker with various states.", + "searchTags": [ + "energy", + "power", + "ev" + ], + "widgetSizeX": 2, + "widgetSizeY": 1, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "element.attr({fill: ctx.properties.backgroundColor});", + "actions": null + }, + { + "tag": "breaker", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "var initial = ctx.values.initialState;\nvar action = initial ? 'offUpdateState' : 'onUpdateState';\n\nctx.api.callAction(event, action, undefined, {\n next: () => {\n ctx.api.setValue('initialState', !initial);\n }\n});" + } + } + }, + { + "tag": "breaker-trigger", + "stateRenderFunction": "element.fill(ctx.properties.disabledColor);\nif (ctx.values.initialState) {\n element.transform({translateX: 0});\n} else {\n element.transform({translateX: -160});\n}", + "actions": null + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "label", + "stateRenderFunction": "if (ctx.properties.label) {\n element.show();\n var label = ctx.values.initialState ? ctx.properties.onLabel : ctx.properties.offLabel;\n ctx.api.font(element, ctx.properties.labelFont, ctx.properties.labelColor);\n ctx.api.text(element, label);\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "label-box", + "stateRenderFunction": "var color = ctx.properties.disabledColor;\nif (ctx.values.initialState) {\n color = ctx.properties.enabledColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "initialState", + "name": "{i18n:scada.symbol.on-off-state}", + "hint": "{i18n:scada.symbol.on-off-state-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.on}", + "falseLabel": "{i18n:scada.symbol.off}", + "stateLabel": "", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "onUpdateState", + "name": "{i18n:scada.symbol.on-update-state}", + "hint": "{i18n:scada.symbol.on-update-state-hint}", + "group": null, + "type": "action", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": { + "action": "EXECUTE_RPC", + "executeRpc": { + "method": "setState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "setAttribute": { + "scope": "SERVER_SCOPE", + "key": "state" + }, + "putTimeSeries": { + "key": "state" + }, + "valueToData": { + "type": "CONSTANT", + "constantValue": true, + "valueToDataFunction": "/* Convert input boolean value to RPC parameters or attribute/time-series value */\nreturn value;" + } + }, + "defaultWidgetActionSettings": null + }, + { + "id": "offUpdateState", + "name": "{i18n:scada.symbol.off-update-state}", + "hint": "{i18n:scada.symbol.off-update-state-hint}", + "group": null, + "type": "action", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": { + "action": "EXECUTE_RPC", + "executeRpc": { + "method": "setState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "setAttribute": { + "key": "state", + "scope": "SERVER_SCOPE" + }, + "putTimeSeries": { + "key": "state" + }, + "valueToData": { + "type": "CONSTANT", + "constantValue": false, + "valueToDataFunction": "/* Convert input boolean value to RPC parameters or attribute/time-series value */\nreturn value;" + } + }, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + } + ], + "properties": [ + { + "id": "label", + "name": "{i18n:scada.symbol.label}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "onLabel", + "name": "{i18n:scada.symbol.label}", + "type": "text", + "default": "ON", + "disabled": false, + "visible": true + }, + { + "id": "offLabel", + "name": "{i18n:scada.symbol.label}", + "type": "text", + "default": "OFF", + "disabled": false, + "visible": true + }, + { + "id": "labelFont", + "name": "{i18n:scada.symbol.label}", + "type": "font", + "default": { + "size": 42, + "sizeUnit": "px", + "family": "Roboto", + "weight": "400", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "labelColor", + "name": "{i18n:scada.symbol.label}", + "type": "color", + "default": "#1A1A1A", + "disabled": false, + "visible": true + }, + { + "id": "backgroundColor", + "name": "{i18n:scada.symbol.background-color}", + "type": "color", + "default": "#FFFFFF", + "disabled": false, + "visible": true + }, + { + "id": "enabledColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "subLabel": "{i18n:scada.symbol.enabled}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "disabledColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "subLabel": "{i18n:scada.symbol.disabled}", + "disabled": false, + "visible": true + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + } + ] +} + + + ON + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/horizontal-energy-system-controller-hp.svg b/application/src/main/data/json/system/scada_symbols/horizontal-energy-system-controller-hp.svg new file mode 100644 index 0000000000..13ee85821b --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/horizontal-energy-system-controller-hp.svg @@ -0,0 +1,378 @@ +{ + "title": "HP Horizontal energy systems controller", + "description": "Horizontal energy systems controller with various states.", + "searchTags": [ + "energy", + "power", + "monitoring" + ], + "widgetSizeX": 3, + "widgetSizeY": 2, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.disconnectedColor;\nif (ctx.values.connected) {\n color = ctx.properties.connectedColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "indicator", + "stateRenderFunction": "var color = ctx.properties.disconnectedIndicatorColor;\nif (ctx.values.connected) {\n color = ctx.properties.connectedIndicatorColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "label", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.labelFont, ctx.properties.labelColor);\n ctx.api.text(element, ctx.properties.label);", + "actions": null + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "connected", + "name": "{i18n:scada.symbol.connected}", + "hint": "{i18n:scada.symbol.connected-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.connected}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "connected" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "connectedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "subLabel": "{i18n:scada.symbol.connected}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "disconnectedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "subLabel": "{i18n:scada.symbol.disconnected}", + "disabled": false, + "visible": true + }, + { + "id": "connectedIndicatorColor", + "name": "{i18n:scada.symbol.indicator}", + "type": "color", + "default": "#198038", + "subLabel": "{i18n:scada.symbol.connected}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "disconnectedIndicatorColor", + "name": "{i18n:scada.symbol.indicator}", + "type": "color", + "default": "#DEDEDE", + "subLabel": "{i18n:scada.symbol.disconnected}", + "disabled": false, + "visible": true + }, + { + "id": "label", + "name": "{i18n:scada.symbol.label}", + "type": "text", + "default": "Connected", + "fieldClass": "medium-width", + "disabled": false, + "visible": true + }, + { + "id": "labelFont", + "name": "{i18n:scada.symbol.label}", + "type": "font", + "default": { + "size": 30, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "labelColor", + "name": "{i18n:scada.symbol.label}", + "type": "color", + "default": "#000", + "disabled": false, + "visible": true + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + } + ] +} +Connected + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/house-hp.svg b/application/src/main/data/json/system/scada_symbols/house-hp.svg new file mode 100644 index 0000000000..d95c47bb8b --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/house-hp.svg @@ -0,0 +1,349 @@ +{ + "title": "HP House", + "description": "House with various states.", + "searchTags": [ + "power", + "energy", + "consumer" + ], + "widgetSizeX": 2, + "widgetSizeY": 2, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "required": null, + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "required": null, + "subLabel": "{i18n:scada.symbol.stopped}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "required": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "required": null, + "subLabel": "{i18n:scada.symbol.critical}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + } + ] +} + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/industrial-fuel-generator-hp.svg b/application/src/main/data/json/system/scada_symbols/industrial-fuel-generator-hp.svg new file mode 100644 index 0000000000..2b7a476a92 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/industrial-fuel-generator-hp.svg @@ -0,0 +1,358 @@ +{ + "title": "HP Industrial fuel generator", + "description": "Industrial fuel generator with various states.", + "searchTags": [ + "power", + "energy", + "fuel", + "generation" + ], + "widgetSizeX": 5, + "widgetSizeY": 3, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "sub-background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = '#999999';\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "required": null, + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null, + "disabled": false, + "visible": true + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "required": null, + "subLabel": "{i18n:scada.symbol.stopped}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null, + "disabled": false, + "visible": true + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "required": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null, + "disabled": false, + "visible": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "required": null, + "subLabel": "{i18n:scada.symbol.critical}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null, + "disabled": false, + "visible": true + } + ] +} + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/inverter-hp.svg b/application/src/main/data/json/system/scada_symbols/inverter-hp.svg new file mode 100644 index 0000000000..73f14c8e75 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/inverter-hp.svg @@ -0,0 +1,578 @@ +{ + "title": "HP Inverter", + "description": "Inverter with various states.", + "searchTags": [ + "energy", + "power", + "hybrid", + "solar", + "backup" + ], + "widgetSizeX": 3, + "widgetSizeY": 3, + "tags": [ + { + "tag": "absorption", + "stateRenderFunction": "if (ctx.values.running && ctx.values.chargingMode === 2) {\n element.fill(ctx.properties.chargingIndicatorsColor);\n} else {\n element.fill('#dedede');\n}", + "actions": null + }, + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "bulk", + "stateRenderFunction": "if (ctx.values.running && ctx.values.chargingMode === 1) {\n element.fill(ctx.properties.chargingIndicatorsColor);\n} else {\n element.fill('#dedede');\n}", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "float", + "stateRenderFunction": "if (ctx.values.running && ctx.values.chargingMode === 3) {\n element.fill(ctx.properties.chargingIndicatorsColor);\n} else {\n element.fill('#dedede');\n}", + "actions": null + }, + { + "tag": "inverterOn", + "stateRenderFunction": "if (ctx.values.running && !ctx.values.operationMode) {\n element.fill(ctx.properties.operationIndicatorsColor);\n} else {\n element.fill('#dedede');\n}", + "actions": null + }, + { + "tag": "lowBattery", + "stateRenderFunction": "if (ctx.values.running && ctx.values.lowBattery) {\n element.fill(ctx.properties.faultIndicatorsColor);\n} else {\n element.fill('#dedede');\n}", + "actions": null + }, + { + "tag": "mainsOn", + "stateRenderFunction": "if (ctx.values.running && ctx.values.operationMode) {\n element.fill(ctx.properties.operationIndicatorsColor);\n} else {\n element.fill('#dedede');\n}", + "actions": null + }, + { + "tag": "overload", + "stateRenderFunction": "if (ctx.values.running && ctx.values.overload) {\n element.fill(ctx.properties.faultIndicatorsColor);\n} else {\n element.fill('#dedede');\n}", + "actions": null + }, + { + "tag": "temperature", + "stateRenderFunction": "if (ctx.values.running && ctx.values.temperature) {\n element.fill(ctx.properties.faultIndicatorsColor);\n} else {\n element.fill('#dedede');\n}", + "actions": null + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "operationMode", + "name": "{i18n:scada.symbol.operation-mode}", + "hint": "{i18n:scada.symbol.operation-mode-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.mains-on-mode}", + "falseLabel": "{i18n:scada.symbol.inverter-on-mode}", + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": true, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "chargingMode", + "name": "{i18n:scada.symbol.charging-mode}", + "hint": "{i18n:scada.symbol.charging-mode-hint}", + "group": null, + "type": "value", + "valueType": "INTEGER", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": null, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "chargingMode" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "overload", + "name": "{i18n:scada.symbol.overload-fault}", + "hint": "{i18n:scada.symbol.overload-fault-hint}", + "group": "{i18n:scada.symbol.inverter-faults}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "lowBattery", + "name": "{i18n:scada.symbol.low-battery-fault}", + "hint": "{i18n:scada.symbol.low-battery-fault-hint}", + "group": "{i18n:scada.symbol.inverter-faults}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "temperature", + "name": "{i18n:scada.symbol.temperature-fault}", + "hint": "{i18n:scada.symbol.temperature-fault-hint}", + "group": "{i18n:scada.symbol.inverter-faults}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "subLabel": "{i18n:scada.symbol.stopped}", + "disabled": false, + "visible": true + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + }, + { + "id": "operationIndicatorsColor", + "name": "{i18n:scada.symbol.operation-mode-indicators-color}", + "type": "color", + "default": "#198038", + "disabled": false, + "visible": true + }, + { + "id": "chargingIndicatorsColor", + "name": "{i18n:scada.symbol.charging-mode-indicators-color}", + "type": "color", + "default": "#FAA405", + "disabled": false, + "visible": true + }, + { + "id": "faultIndicatorsColor", + "name": "{i18n:scada.symbol.inverter-fault-indicators-color}", + "type": "color", + "default": "#D12730", + "disabled": false, + "visible": true + } + ] +} + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/large-inverter-hp.svg b/application/src/main/data/json/system/scada_symbols/large-inverter-hp.svg new file mode 100644 index 0000000000..11bc5da0a6 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/large-inverter-hp.svg @@ -0,0 +1,578 @@ +{ + "title": "HP Large inverter", + "description": "Large inverter with various states.", + "searchTags": [ + "energy", + "power", + "hybrid", + "solar", + "backup" + ], + "widgetSizeX": 6, + "widgetSizeY": 3, + "tags": [ + { + "tag": "absorption", + "stateRenderFunction": "if (ctx.values.running && ctx.values.chargingMode === 2) {\n element.fill(ctx.properties.chargingIndicatorsColor);\n} else {\n element.fill('#dedede');\n}", + "actions": null + }, + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "bulk", + "stateRenderFunction": "if (ctx.values.running && ctx.values.chargingMode === 1) {\n element.fill(ctx.properties.chargingIndicatorsColor);\n} else {\n element.fill('#dedede');\n}", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "float", + "stateRenderFunction": "if (ctx.values.running && ctx.values.chargingMode === 3) {\n element.fill(ctx.properties.chargingIndicatorsColor);\n} else {\n element.fill('#dedede');\n}", + "actions": null + }, + { + "tag": "inverterOn", + "stateRenderFunction": "if (ctx.values.running && !ctx.values.operationMode) {\n element.fill(ctx.properties.operationIndicatorsColor);\n} else {\n element.fill('#dedede');\n}", + "actions": null + }, + { + "tag": "lowBattery", + "stateRenderFunction": "if (ctx.values.running && ctx.values.lowBattery) {\n element.fill(ctx.properties.faultIndicatorsColor);\n} else {\n element.fill('#dedede');\n}", + "actions": null + }, + { + "tag": "mainsOn", + "stateRenderFunction": "if (ctx.values.running && ctx.values.operationMode) {\n element.fill(ctx.properties.operationIndicatorsColor);\n} else {\n element.fill('#dedede');\n}", + "actions": null + }, + { + "tag": "overload", + "stateRenderFunction": "if (ctx.values.running && ctx.values.overload) {\n element.fill(ctx.properties.faultIndicatorsColor);\n} else {\n element.fill('#dedede');\n}", + "actions": null + }, + { + "tag": "temperature", + "stateRenderFunction": "if (ctx.values.running && ctx.values.temperature) {\n element.fill(ctx.properties.faultIndicatorsColor);\n} else {\n element.fill('#dedede');\n}", + "actions": null + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "operationMode", + "name": "{i18n:scada.symbol.operation-mode}", + "hint": "{i18n:scada.symbol.operation-mode-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.mains-on-mode}", + "falseLabel": "{i18n:scada.symbol.inverter-on-mode}", + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": true, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "chargingMode", + "name": "{i18n:scada.symbol.charging-mode}", + "hint": "{i18n:scada.symbol.charging-mode-hint}", + "group": null, + "type": "value", + "valueType": "INTEGER", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": null, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "chargingMode" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "overload", + "name": "{i18n:scada.symbol.overload-fault}", + "hint": "{i18n:scada.symbol.overload-fault-hint}", + "group": "{i18n:scada.symbol.inverter-faults}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "lowBattery", + "name": "{i18n:scada.symbol.low-battery-fault}", + "hint": "{i18n:scada.symbol.low-battery-fault-hint}", + "group": "{i18n:scada.symbol.inverter-faults}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "temperature", + "name": "{i18n:scada.symbol.temperature-fault}", + "hint": "{i18n:scada.symbol.temperature-fault-hint}", + "group": "{i18n:scada.symbol.inverter-faults}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "subLabel": "{i18n:scada.symbol.stopped}", + "disabled": false, + "visible": true + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + }, + { + "id": "operationIndicatorsColor", + "name": "{i18n:scada.symbol.operation-mode-indicators-color}", + "type": "color", + "default": "#198038", + "disabled": false, + "visible": true + }, + { + "id": "chargingIndicatorsColor", + "name": "{i18n:scada.symbol.charging-mode-indicators-color}", + "type": "color", + "default": "#FAA405", + "disabled": false, + "visible": true + }, + { + "id": "faultIndicatorsColor", + "name": "{i18n:scada.symbol.inverter-fault-indicators-color}", + "type": "color", + "default": "#D12730", + "disabled": false, + "visible": true + } + ] +} + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/low-voltage-tower-hp.svg b/application/src/main/data/json/system/scada_symbols/low-voltage-tower-hp.svg new file mode 100644 index 0000000000..812e616433 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/low-voltage-tower-hp.svg @@ -0,0 +1,278 @@ +{ + "title": "HP Low voltage tower", + "description": "Low voltage tower with various states.", + "searchTags": [ + "power", + "energy" + ], + "widgetSizeX": 2, + "widgetSizeY": 4, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "element.attr({fill: ctx.properties.backgroundColor});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "backgroundColor", + "name": "{i18n:scada.symbol.background-color}", + "type": "color", + "default": "#999999", + "divider": false, + "disabled": false, + "visible": true + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + } + ] +} + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/low-voltage-transformer-tower-hp.svg b/application/src/main/data/json/system/scada_symbols/low-voltage-transformer-tower-hp.svg new file mode 100644 index 0000000000..2985dc600c --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/low-voltage-transformer-tower-hp.svg @@ -0,0 +1,334 @@ +{ + "title": "HP Low voltage transformer tower", + "description": "Low voltage transformer tower with various states.", + "searchTags": [ + "power", + "energy" + ], + "widgetSizeX": 2, + "widgetSizeY": 4, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "element.attr({fill: ctx.properties.backgroundColor});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "transformer", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "backgroundColor", + "name": "{i18n:scada.symbol.background-color}", + "type": "color", + "default": "#999999" + }, + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "subLabel": "{i18n:scada.symbol.running}", + "divider": true + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "subLabel": "{i18n:scada.symbol.stopped}" + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}" + } + ] +} + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/manufacture-hp.svg b/application/src/main/data/json/system/scada_symbols/manufacture-hp.svg new file mode 100644 index 0000000000..5b35f2eaf2 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/manufacture-hp.svg @@ -0,0 +1,344 @@ +{ + "title": "HP Manufacture", + "description": "Manufacture with various states.", + "searchTags": [ + "power", + "energy", + "consumer" + ], + "widgetSizeX": 2, + "widgetSizeY": 2, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "required": null, + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "required": null, + "subLabel": "{i18n:scada.symbol.stopped}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "required": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "required": null, + "subLabel": "{i18n:scada.symbol.critical}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + } + ] +} + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/power-socket-hp.svg b/application/src/main/data/json/system/scada_symbols/power-socket-hp.svg new file mode 100644 index 0000000000..5524b3bb14 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/power-socket-hp.svg @@ -0,0 +1,371 @@ +{ + "title": "HP Power socket", + "description": "Power socket with various states.", + "searchTags": [ + "EU socket", + "US socket", + "Middle East socket", + "energy" + ], + "widgetSizeX": 1, + "widgetSizeY": 1, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "eu-socket", + "stateRenderFunction": "if (ctx.properties.socketType === 'eu') {\n element.show();\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "middle-east-socket", + "stateRenderFunction": "if (ctx.properties.socketType === 'middleEast') {\n element.show();\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "us-socket", + "stateRenderFunction": "if (ctx.properties.socketType === 'us') {\n element.show();\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "socketType", + "name": "{i18n:scada.symbol.socket}", + "type": "select", + "default": "eu", + "fieldClass": "flex", + "items": [ + { + "value": "eu", + "label": "EU" + }, + { + "value": "us", + "label": "US" + }, + { + "value": "middleEast", + "label": "Middle East" + } + ], + "disabled": false, + "visible": true + }, + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "subLabel": "{i18n:scada.symbol.stopped}", + "disabled": false, + "visible": true + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + } + ] +} + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/power-transformer-hp.svg b/application/src/main/data/json/system/scada_symbols/power-transformer-hp.svg new file mode 100644 index 0000000000..67e75be827 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/power-transformer-hp.svg @@ -0,0 +1,359 @@ +{ + "title": "HP Power transformer", + "description": "Power transformer with various states.", + "searchTags": [ + "power", + "energy" + ], + "widgetSizeX": 2, + "widgetSizeY": 3, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "required": null, + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "required": null, + "subLabel": "{i18n:scada.symbol.stopped}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "required": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "required": null, + "subLabel": "{i18n:scada.symbol.critical}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + } + ] +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/single-key-switch-hp.svg b/application/src/main/data/json/system/scada_symbols/single-key-switch-hp.svg new file mode 100644 index 0000000000..77d6e7f95d --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/single-key-switch-hp.svg @@ -0,0 +1,373 @@ +{ + "title": "HP Single-key switch", + "description": "Single-key switch with various states.", + "searchTags": [ + "energy", + "power" + ], + "widgetSizeX": 1, + "widgetSizeY": 1, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "element.attr({fill: ctx.properties.backgroundColor});", + "actions": null + }, + { + "tag": "button", + "stateRenderFunction": "var color = ctx.properties.disabledColor;\nif (ctx.values.initialState) {\n color = ctx.properties.enabledColor;\n}\nelement.attr({fill: color});", + "actions": { + "click": { + "actionFunction": "var initial = ctx.values.initialState;\nvar action = initial ? 'offUpdateState' : 'onUpdateState';\n\nctx.api.callAction(event, action, undefined, {\n next: () => {\n ctx.api.setValue('initialState', !initial);\n }\n});" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "initialState", + "name": "{i18n:scada.symbol.on-off-state}", + "hint": "{i18n:scada.symbol.on-off-state-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.on}", + "falseLabel": "{i18n:scada.symbol.off}", + "stateLabel": "", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "onUpdateState", + "name": "{i18n:scada.symbol.on-update-state}", + "hint": "{i18n:scada.symbol.on-update-state-hint}", + "group": null, + "type": "action", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": { + "action": "EXECUTE_RPC", + "executeRpc": { + "method": "setState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "setAttribute": { + "scope": "SERVER_SCOPE", + "key": "state" + }, + "putTimeSeries": { + "key": "state" + }, + "valueToData": { + "type": "CONSTANT", + "constantValue": true, + "valueToDataFunction": "/* Convert input boolean value to RPC parameters or attribute/time-series value */\nreturn value;" + } + }, + "defaultWidgetActionSettings": null + }, + { + "id": "offUpdateState", + "name": "{i18n:scada.symbol.off-update-state}", + "hint": "{i18n:scada.symbol.off-update-state-hint}", + "group": null, + "type": "action", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": { + "action": "EXECUTE_RPC", + "executeRpc": { + "method": "setState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "setAttribute": { + "key": "state", + "scope": "SERVER_SCOPE" + }, + "putTimeSeries": { + "key": "state" + }, + "valueToData": { + "type": "CONSTANT", + "constantValue": false, + "valueToDataFunction": "/* Convert input boolean value to RPC parameters or attribute/time-series value */\nreturn value;" + } + }, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + } + ], + "properties": [ + { + "id": "backgroundColor", + "name": "{i18n:scada.symbol.background-color}", + "type": "color", + "default": "#FFFFFF", + "disabled": false, + "visible": true + }, + { + "id": "enabledColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "subLabel": "{i18n:scada.symbol.enabled}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "disabledColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "subLabel": "{i18n:scada.symbol.disabled}", + "disabled": false, + "visible": true + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + } + ] +} + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/small-power-transformer-hp.svg b/application/src/main/data/json/system/scada_symbols/small-power-transformer-hp.svg new file mode 100644 index 0000000000..049c75ab58 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/small-power-transformer-hp.svg @@ -0,0 +1,359 @@ +{ + "title": "HP Small power transformer", + "description": "Small power transformer with various states.", + "searchTags": [ + "power", + "energy" + ], + "widgetSizeX": 2, + "widgetSizeY": 2, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "required": null, + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "required": null, + "subLabel": "{i18n:scada.symbol.stopped}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "required": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "required": null, + "subLabel": "{i18n:scada.symbol.critical}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + } + ] +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/solar-panel-hp.svg b/application/src/main/data/json/system/scada_symbols/solar-panel-hp.svg new file mode 100644 index 0000000000..b7b7724f89 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/solar-panel-hp.svg @@ -0,0 +1,353 @@ +{ + "title": "HP Solar panel", + "description": "Solar panel with various states.", + "searchTags": [ + "energy", + "power", + "renewable", + "generation" + ], + "widgetSizeX": 1, + "widgetSizeY": 2, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "required": null, + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null, + "disabled": false, + "visible": true + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "required": null, + "subLabel": "{i18n:scada.symbol.stopped}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null, + "disabled": false, + "visible": true + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "required": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null, + "disabled": false, + "visible": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "required": null, + "subLabel": "{i18n:scada.symbol.critical}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null, + "disabled": false, + "visible": true + } + ] +} + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/stand-solar-panel-hp.svg b/application/src/main/data/json/system/scada_symbols/stand-solar-panel-hp.svg new file mode 100644 index 0000000000..93e622a021 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/stand-solar-panel-hp.svg @@ -0,0 +1,357 @@ +{ + "title": "HP Stand solar panel", + "description": "Stand solar panel with various states.", + "searchTags": [ + "energy", + "power", + "renewable", + "generation" + ], + "widgetSizeX": 3, + "widgetSizeY": 2, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "required": null, + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null, + "disabled": false, + "visible": true + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "required": null, + "subLabel": "{i18n:scada.symbol.stopped}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null, + "disabled": false, + "visible": true + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "required": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null, + "disabled": false, + "visible": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "required": null, + "subLabel": "{i18n:scada.symbol.critical}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null, + "disabled": false, + "visible": true + } + ] +} + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/three-rate-energy-meter-hp.svg b/application/src/main/data/json/system/scada_symbols/three-rate-energy-meter-hp.svg new file mode 100644 index 0000000000..f2b7bf4d54 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/three-rate-energy-meter-hp.svg @@ -0,0 +1,747 @@ +{ + "title": "HP Three-rate energy meter", + "description": "Three-rate energy meter with various states.", + "searchTags": [ + "power", + "energy" + ], + "widgetSizeX": 3, + "widgetSizeY": 2, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "night-label", + "stateRenderFunction": "if (ctx.properties.showNightLabel) {\n element.show();\n ctx.api.font(element, ctx.properties.nightLabelFont, ctx.properties.nightLabelColor);\n ctx.api.text(element, ctx.properties.nightLabel);\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "night-rate", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.nightValueFont, ctx.properties.nightValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.nightRate, 0, null, 0));", + "actions": null + }, + { + "tag": "off-peak-label", + "stateRenderFunction": "if (ctx.properties.showOffPeakLabel) {\n element.show();\n ctx.api.font(element, ctx.properties.offPeakLabelFont, ctx.properties.offPeakLabelColor);\n ctx.api.text(element, ctx.properties.offPeakLabel);\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "off-peak-rate", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.offPeakValueFont, ctx.properties.offPeakValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.offPeakRate, 0, null, 0));", + "actions": null + }, + { + "tag": "peak-label", + "stateRenderFunction": "if (ctx.properties.showPeakLabel) {\n element.show();\n ctx.api.font(element, ctx.properties.peakLabelFont, ctx.properties.peakLabelColor);\n ctx.api.text(element, ctx.properties.peakLabel);\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "peak-rate", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.peakValueFont, ctx.properties.peakValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.peakRate, 0, null, 0));", + "actions": null + }, + { + "tag": "units", + "stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.properties.units);\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "value-box-night", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.nightValueBoxBackground;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "value-box-off-peak", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.offPeakValueBoxBackground;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "value-box-peak", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.peakValueBoxBackground;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "offPeakRate", + "name": "{i18n:scada.symbol.off-peak-rate}", + "hint": "{i18n:scada.symbol.measured-hint}", + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "GET_TIME_SERIES", + "defaultValue": null, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "offPeakRate" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "nightRate", + "name": "{i18n:scada.symbol.night-rate}", + "hint": "{i18n:scada.symbol.measured-hint}", + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "GET_TIME_SERIES", + "defaultValue": null, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "nightRate" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "peakRate", + "name": "{i18n:scada.symbol.peak-rate}", + "hint": "{i18n:scada.symbol.measured-hint}", + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "GET_TIME_SERIES", + "defaultValue": null, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "peakRate" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "subLabel": "{i18n:scada.symbol.stopped}", + "disabled": false, + "visible": true + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + }, + { + "id": "showOffPeakLabel", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.off-peak-rate}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "offPeakLabel", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.off-peak-rate}", + "type": "text", + "default": "T1", + "fieldClass": "medium-width", + "disabled": false, + "visible": true + }, + { + "id": "offPeakLabelFont", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.off-peak-rate}", + "type": "font", + "default": { + "size": 36, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "offPeakLabelColor", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.off-peak-rate}", + "type": "color", + "default": "#000000", + "disabled": false, + "visible": true + }, + { + "id": "offPeakValueFont", + "name": "{i18n:scada.symbol.value}", + "group": "{i18n:scada.symbol.off-peak-rate}", + "type": "font", + "default": { + "size": 48, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "offPeakValueColor", + "name": "{i18n:scada.symbol.value}", + "group": "{i18n:scada.symbol.off-peak-rate}", + "type": "color", + "default": "#002878", + "disabled": false, + "visible": true + }, + { + "id": "offPeakValueBoxBackground", + "name": "{i18n:scada.symbol.value-box-background}", + "group": "{i18n:scada.symbol.off-peak-rate}", + "type": "color", + "default": "#DEDEDE", + "disabled": false, + "visible": true + }, + { + "id": "showNightLabel", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.night-rate}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "nightLabel", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.night-rate}", + "type": "text", + "default": "T2", + "fieldClass": "medium-width", + "disabled": false, + "visible": true + }, + { + "id": "nightLabelFont", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.night-rate}", + "type": "font", + "default": { + "size": 36, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "nightLabelColor", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.night-rate}", + "type": "color", + "default": "#000", + "disabled": false, + "visible": true + }, + { + "id": "nightValueFont", + "name": "{i18n:scada.symbol.value}", + "group": "{i18n:scada.symbol.night-rate}", + "type": "font", + "default": { + "size": 48, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "nightValueColor", + "name": "{i18n:scada.symbol.value}", + "group": "{i18n:scada.symbol.night-rate}", + "type": "color", + "default": "#002878", + "disabled": false, + "visible": true + }, + { + "id": "nightValueBoxBackground", + "name": "{i18n:scada.symbol.value-box-background}", + "group": "{i18n:scada.symbol.night-rate}", + "type": "color", + "default": "#DEDEDE", + "disabled": false, + "visible": true + }, + { + "id": "showPeakLabel", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.peak-rate}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "peakLabel", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.peak-rate}", + "type": "text", + "default": "T3", + "fieldClass": "medium-width", + "disabled": false, + "visible": true + }, + { + "id": "peakLabelFont", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.peak-rate}", + "type": "font", + "default": { + "size": 36, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "peakLabelColor", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.peak-rate}", + "type": "color", + "default": "#000", + "disabled": false, + "visible": true + }, + { + "id": "peakValueFont", + "name": "{i18n:scada.symbol.value}", + "group": "{i18n:scada.symbol.peak-rate}", + "type": "font", + "default": { + "size": 48, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "peakValueColor", + "name": "{i18n:scada.symbol.value}", + "group": "{i18n:scada.symbol.peak-rate}", + "type": "color", + "default": "#002878", + "disabled": false, + "visible": true + }, + { + "id": "peakValueBoxBackground", + "name": "{i18n:scada.symbol.value-box-background}", + "group": "{i18n:scada.symbol.peak-rate}", + "type": "color", + "default": "#DEDEDE", + "disabled": false, + "visible": true + }, + { + "id": "showUnits", + "name": "{i18n:scada.symbol.units}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "units", + "name": "{i18n:scada.symbol.units}", + "type": "units", + "default": "kWh", + "disabled": false, + "visible": true + }, + { + "id": "unitsFont", + "name": "{i18n:scada.symbol.units}", + "type": "font", + "default": { + "size": 36, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "unitsColor", + "name": "{i18n:scada.symbol.units}", + "type": "color", + "default": "#000", + "disabled": false, + "visible": true + } + ] +} +T1T2T3000223000223000223kWh + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/top-light-bulb-hp.svg b/application/src/main/data/json/system/scada_symbols/top-light-bulb-hp.svg new file mode 100644 index 0000000000..ed6855884a --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/top-light-bulb-hp.svg @@ -0,0 +1,346 @@ +{ + "title": "HP Top light bulb", + "description": "Top light bulb with various states.", + "searchTags": [ + "energy" + ], + "widgetSizeX": 1, + "widgetSizeY": 1, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "required": null, + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "required": null, + "subLabel": "{i18n:scada.symbol.stopped}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "required": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "required": null, + "subLabel": "{i18n:scada.symbol.critical}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + } + ] +} + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/two-key-switch-hp.svg b/application/src/main/data/json/system/scada_symbols/two-key-switch-hp.svg new file mode 100644 index 0000000000..40e04a3bf5 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/two-key-switch-hp.svg @@ -0,0 +1,489 @@ +{ + "title": "HP Two-key switch", + "description": "Two-key switch with various states.", + "searchTags": [ + "energy", + "power" + ], + "widgetSizeX": 1, + "widgetSizeY": 1, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "element.attr({fill: ctx.properties.backgroundColor});", + "actions": null + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "left-button", + "stateRenderFunction": "var color = ctx.properties.disabledColor;\nif (ctx.values.initialStateLeftButton) {\n color = ctx.properties.enabledColor;\n}\nelement.attr({fill: color});", + "actions": { + "click": { + "actionFunction": "var initial = ctx.values.initialStateLeftButton;\nvar action = initial ? 'offUpdateStateLeftButton' : 'onUpdateStateLeftButton';\n\nctx.api.callAction(event, action, undefined, {\n next: () => {\n ctx.api.setValue('initialStateLeftButton', !initial);\n }\n});" + } + } + }, + { + "tag": "right-button", + "stateRenderFunction": "var color = ctx.properties.disabledColor;\nif (ctx.values.initialStateRightButton) {\n color = ctx.properties.enabledColor;\n}\nelement.attr({fill: color});", + "actions": { + "click": { + "actionFunction": "var initial = ctx.values.initialStateRightButton;\nvar action = initial ? 'offUpdateStateRightButton' : 'onUpdateStateRightButton';\n\nctx.api.callAction(event, action, undefined, {\n next: () => {\n ctx.api.setValue('initialStateRightButton', !initial);\n }\n});" + } + } + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "initialStateLeftButton", + "name": "{i18n:scada.symbol.on-off-state}", + "hint": "{i18n:scada.symbol.on-off-state-hint}", + "group": "{i18n:scada.symbol.left-button}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.on}", + "falseLabel": "{i18n:scada.symbol.off}", + "stateLabel": "", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "leftButton" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "onUpdateStateLeftButton", + "name": "{i18n:scada.symbol.on-update-state}", + "hint": "{i18n:scada.symbol.on-update-state-hint}", + "group": "{i18n:scada.symbol.left-button}", + "type": "action", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": { + "action": "EXECUTE_RPC", + "executeRpc": { + "method": "setState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "setAttribute": { + "scope": "SERVER_SCOPE", + "key": "state" + }, + "putTimeSeries": { + "key": "state" + }, + "valueToData": { + "type": "CONSTANT", + "constantValue": true, + "valueToDataFunction": "/* Convert input boolean value to RPC parameters or attribute/time-series value */\nreturn value;" + } + }, + "defaultWidgetActionSettings": null + }, + { + "id": "offUpdateStateLeftButton", + "name": "{i18n:scada.symbol.off-update-state}", + "hint": "{i18n:scada.symbol.off-update-state-hint}", + "group": "{i18n:scada.symbol.left-button}", + "type": "action", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": { + "action": "EXECUTE_RPC", + "executeRpc": { + "method": "setState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "setAttribute": { + "key": "state", + "scope": "SERVER_SCOPE" + }, + "putTimeSeries": { + "key": "state" + }, + "valueToData": { + "type": "CONSTANT", + "constantValue": false, + "valueToDataFunction": "/* Convert input boolean value to RPC parameters or attribute/time-series value */\nreturn value;" + } + }, + "defaultWidgetActionSettings": null + }, + { + "id": "initialStateRightButton", + "name": "{i18n:scada.symbol.on-off-state}", + "hint": "{i18n:scada.symbol.on-off-state-hint}", + "group": "{i18n:scada.symbol.right-button}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.on}", + "falseLabel": "{i18n:scada.symbol.off}", + "stateLabel": null, + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "rightButton" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "onUpdateStateRightButton", + "name": "{i18n:scada.symbol.on-update-state}", + "hint": "{i18n:scada.symbol.on-update-state-hint}", + "group": "{i18n:scada.symbol.right-button}", + "type": "action", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": { + "action": "EXECUTE_RPC", + "executeRpc": { + "method": "setState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "setAttribute": { + "scope": "SERVER_SCOPE", + "key": "state" + }, + "putTimeSeries": { + "key": "state" + }, + "valueToData": { + "type": "CONSTANT", + "constantValue": true, + "valueToDataFunction": "/* Convert input boolean value to RPC parameters or attribute/time-series value */\nreturn value;" + } + }, + "defaultWidgetActionSettings": null + }, + { + "id": "offUpdateStateRightButton", + "name": "{i18n:scada.symbol.off-update-state}", + "hint": "{i18n:scada.symbol.off-update-state-hint}", + "group": "{i18n:scada.symbol.right-button}", + "type": "action", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": { + "action": "EXECUTE_RPC", + "executeRpc": { + "method": "setState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "setAttribute": { + "key": "state", + "scope": "SERVER_SCOPE" + }, + "putTimeSeries": { + "key": "state" + }, + "valueToData": { + "type": "CONSTANT", + "constantValue": false, + "valueToDataFunction": "/* Convert input boolean value to RPC parameters or attribute/time-series value */\nreturn value;" + } + }, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + } + ], + "properties": [ + { + "id": "backgroundColor", + "name": "{i18n:scada.symbol.background-color}", + "type": "color", + "default": "#FFFFFF", + "disabled": false, + "visible": true + }, + { + "id": "enabledColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "subLabel": "{i18n:scada.symbol.enabled}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "disabledColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "subLabel": "{i18n:scada.symbol.disabled}", + "disabled": false, + "visible": true + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + } + ] +} + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/two-rate-energy-meter-hp.svg b/application/src/main/data/json/system/scada_symbols/two-rate-energy-meter-hp.svg new file mode 100644 index 0000000000..7adc401836 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/two-rate-energy-meter-hp.svg @@ -0,0 +1,618 @@ +{ + "title": "HP Two-rate energy meter", + "description": "Two-rate energy meter with various states.", + "searchTags": [ + "power", + "energy" + ], + "widgetSizeX": 2, + "widgetSizeY": 2, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "day-label", + "stateRenderFunction": "if (ctx.properties.showDayLabel) {\n element.show();\n ctx.api.font(element, ctx.properties.dayLabelFont, ctx.properties.dayLabelColor);\n ctx.api.text(element, ctx.properties.dayLabel);\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "day-rate", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.dayValueFont, ctx.properties.dayValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.dayRate, 0, null, 0));", + "actions": null + }, + { + "tag": "night-label", + "stateRenderFunction": "if (ctx.properties.showNightLabel) {\n element.show();\n ctx.api.font(element, ctx.properties.nightLabelFont, ctx.properties.nightLabelColor);\n ctx.api.text(element, ctx.properties.nightLabel);\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "night-rate", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.nightValueFont, ctx.properties.nightValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.nightRate, 0, null, 0));", + "actions": null + }, + { + "tag": "units", + "stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.properties.units);\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "value-box-day", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.dayValueBoxBackground;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "value-box-night", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.nightValueBoxBackground;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "dayRate", + "name": "{i18n:scada.symbol.day-rate}", + "hint": "{i18n:scada.symbol.measured-hint}", + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "GET_TIME_SERIES", + "defaultValue": null, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "dayRate" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "nightRate", + "name": "{i18n:scada.symbol.night-rate}", + "hint": "{i18n:scada.symbol.measured-hint}", + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "GET_TIME_SERIES", + "defaultValue": null, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "nightRate" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "subLabel": "{i18n:scada.symbol.stopped}", + "disabled": false, + "visible": true + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + }, + { + "id": "showDayLabel", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.day-rate}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "dayLabel", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.day-rate}", + "type": "text", + "default": "T1", + "fieldClass": "medium-width", + "disabled": false, + "visible": true + }, + { + "id": "dayLabelFont", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.day-rate}", + "type": "font", + "default": { + "size": 36, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "dayLabelColor", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.day-rate}", + "type": "color", + "default": "#000000", + "disabled": false, + "visible": true + }, + { + "id": "dayValueFont", + "name": "{i18n:scada.symbol.value}", + "group": "{i18n:scada.symbol.day-rate}", + "type": "font", + "default": { + "size": 48, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "dayValueColor", + "name": "{i18n:scada.symbol.value}", + "group": "{i18n:scada.symbol.day-rate}", + "type": "color", + "default": "#002878", + "disabled": false, + "visible": true + }, + { + "id": "dayValueBoxBackground", + "name": "{i18n:scada.symbol.value-box-background}", + "group": "{i18n:scada.symbol.day-rate}", + "type": "color", + "default": "#DEDEDE", + "disabled": false, + "visible": true + }, + { + "id": "showNightLabel", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.night-rate}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "nightLabel", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.night-rate}", + "type": "text", + "default": "T2", + "fieldClass": "medium-width", + "disabled": false, + "visible": true + }, + { + "id": "nightLabelFont", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.night-rate}", + "type": "font", + "default": { + "size": 36, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "nightLabelColor", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.night-rate}", + "type": "color", + "default": "#000", + "disabled": false, + "visible": true + }, + { + "id": "nightValueFont", + "name": "{i18n:scada.symbol.value}", + "group": "{i18n:scada.symbol.night-rate}", + "type": "font", + "default": { + "size": 48, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "nightValueColor", + "name": "{i18n:scada.symbol.value}", + "group": "{i18n:scada.symbol.night-rate}", + "type": "color", + "default": "#002878", + "disabled": false, + "visible": true + }, + { + "id": "nightValueBoxBackground", + "name": "{i18n:scada.symbol.value-box-background}", + "group": "{i18n:scada.symbol.night-rate}", + "type": "color", + "default": "#DEDEDE", + "disabled": false, + "visible": true + }, + { + "id": "showUnits", + "name": "{i18n:scada.symbol.units}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "units", + "name": "{i18n:scada.symbol.units}", + "type": "units", + "default": "kWh", + "disabled": false, + "visible": true + }, + { + "id": "unitsFont", + "name": "{i18n:scada.symbol.units}", + "type": "font", + "default": { + "size": 36, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "unitsColor", + "name": "{i18n:scada.symbol.units}", + "type": "color", + "default": "#000", + "disabled": false, + "visible": true + } + ] +} +T1T2000023000023kWh + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/vertical-energy-system-controller-hp.svg b/application/src/main/data/json/system/scada_symbols/vertical-energy-system-controller-hp.svg new file mode 100644 index 0000000000..cebe949c36 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/vertical-energy-system-controller-hp.svg @@ -0,0 +1,378 @@ +{ + "title": "HP Vertical energy systems controller", + "description": "Vertical energy systems controller with various states.", + "searchTags": [ + "energy", + "power", + "monitoring" + ], + "widgetSizeX": 2, + "widgetSizeY": 3, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.disconnectedColor;\nif (ctx.values.connected) {\n color = ctx.properties.connectedColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "indicator", + "stateRenderFunction": "var color = ctx.properties.disconnectedIndicatorColor;\nif (ctx.values.connected) {\n color = ctx.properties.connectedIndicatorColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "label", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.labelFont, ctx.properties.labelColor);\n ctx.api.text(element, ctx.properties.label);", + "actions": null + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "connected", + "name": "{i18n:scada.symbol.connected}", + "hint": "{i18n:scada.symbol.connected-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.connected}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "connected" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "connectedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "subLabel": "{i18n:scada.symbol.connected}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "disconnectedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "subLabel": "{i18n:scada.symbol.disconnected}", + "disabled": false, + "visible": true + }, + { + "id": "connectedIndicatorColor", + "name": "{i18n:scada.symbol.indicator}", + "type": "color", + "default": "#198038", + "subLabel": "{i18n:scada.symbol.connected}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "disconnectedIndicatorColor", + "name": "{i18n:scada.symbol.indicator}", + "type": "color", + "default": "#DEDEDE", + "subLabel": "{i18n:scada.symbol.disconnected}", + "disabled": false, + "visible": true + }, + { + "id": "label", + "name": "{i18n:scada.symbol.label}", + "type": "text", + "default": "Connected", + "fieldClass": "medium-width", + "disabled": false, + "visible": true + }, + { + "id": "labelFont", + "name": "{i18n:scada.symbol.label}", + "type": "font", + "default": { + "size": 30, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "labelColor", + "name": "{i18n:scada.symbol.label}", + "type": "color", + "default": "#000", + "disabled": false, + "visible": true + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + } + ] +} +Connected + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/voltage-relay-hp.svg b/application/src/main/data/json/system/scada_symbols/voltage-relay-hp.svg new file mode 100644 index 0000000000..eaa0b02455 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/voltage-relay-hp.svg @@ -0,0 +1,439 @@ +{ + "title": "HP Voltage relay", + "description": "One phase voltage relay with various states.", + "searchTags": [ + "energy", + "power", + "protection" + ], + "widgetSizeX": 1, + "widgetSizeY": 2, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "units", + "stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.properties.units);\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "value", + "stateRenderFunction": "if (ctx.values.running) {\n element.show();\n ctx.api.font(element, ctx.properties.currentVoltageFont, ctx.properties.currentVoltageColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.voltage, 0, null, 0));\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "value-box", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.valueBoxBackground;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "voltage", + "name": "{i18n:scada.symbol.voltage}", + "hint": "{i18n:scada.symbol.voltage-hint}", + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "GET_TIME_SERIES", + "defaultValue": null, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "voltage" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "subLabel": "{i18n:scada.symbol.stopped}", + "disabled": false, + "visible": true + }, + { + "id": "valueBoxBackground", + "name": "{i18n:scada.symbol.value-box-background}", + "type": "color", + "default": "#DEDEDE", + "disabled": false, + "visible": true + }, + { + "id": "currentVoltageFont", + "name": "{i18n:scada.symbol.current-voltage-color}", + "type": "font", + "default": { + "size": 32, + "sizeUnit": "px", + "family": "Roboto", + "weight": "400", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "currentVoltageColor", + "name": "{i18n:scada.symbol.current-voltage-color}", + "type": "color", + "default": "#002878", + "disabled": false, + "visible": true + }, + { + "id": "showUnits", + "name": "{i18n:scada.symbol.units}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "units", + "name": "{i18n:scada.symbol.units}", + "type": "units", + "default": "V", + "disabled": false, + "visible": true + }, + { + "id": "unitsFont", + "name": "{i18n:scada.symbol.units}", + "type": "font", + "default": { + "size": 24, + "sizeUnit": "px", + "family": "Roboto", + "weight": "500", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "unitsColor", + "name": "{i18n:scada.symbol.units}", + "type": "color", + "default": "#000000", + "disabled": false, + "visible": true + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + } + ] +} +220v + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/voltage-stabilizer-hp.svg b/application/src/main/data/json/system/scada_symbols/voltage-stabilizer-hp.svg new file mode 100644 index 0000000000..aafc2af5a0 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/voltage-stabilizer-hp.svg @@ -0,0 +1,584 @@ +{ + "title": "HP Voltage stabilizer", + "description": "Voltage stabilizer with various states.", + "searchTags": [ + "power", + "energy", + "protection" + ], + "widgetSizeX": 2, + "widgetSizeY": 1, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.operatingMode === 1) {\n color = ctx.properties.runningColor;\n} else if (ctx.values.operatingMode === 2) {\n color = ctx.properties.bypassColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "in-label", + "stateRenderFunction": "if (ctx.properties.showInputLabel) {\n element.show();\n ctx.api.font(element, ctx.properties.inputLabelFont, ctx.properties.inputLabelColor);\n ctx.api.text(element, ctx.properties.inputLabel);\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "in-value", + "stateRenderFunction": "if (ctx.values.operatingMode === 1) {\n element.show();\n ctx.api.font(element, ctx.properties.inputValutFont, ctx.properties.inputValueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.inputVoltage, 0, null, 0));\n} else {\n element.hide();\n}\n", + "actions": null + }, + { + "tag": "in-value-box", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.operatingMode === 1) {\n color = ctx.properties.inputValueBoxBackground;\n} else if (ctx.values.operatingMode === 2) {\n color = ctx.properties.bypassColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "out-label", + "stateRenderFunction": "if (ctx.properties.showOutputLabel) {\n element.show();\n ctx.api.font(element, ctx.properties.outputLabelFont, ctx.properties.outputLabelColor);\n ctx.api.text(element, ctx.properties.outputLabel);\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "out-value", + "stateRenderFunction": "if (ctx.values.operatingMode === 1) {\n element.show();\n ctx.api.font(element, ctx.properties.outputValueFont, ctx.properties.outputValueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.outputVoltage, 0, null, 0));\n} else {\n element.hide();\n}\n", + "actions": null + }, + { + "tag": "out-value-box", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.operatingMode === 1) {\n color = ctx.properties.outputValueBoxBackground;\n} else if (ctx.values.operatingMode === 2) {\n color = ctx.properties.bypassColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "operatingMode", + "name": "{i18n:scada.symbol.operating-mode}", + "hint": "{i18n:scada.symbol.operating-mode-hint}", + "group": null, + "type": "value", + "valueType": "INTEGER", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": null, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "operatingMode" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "inputVoltage", + "name": "{i18n:scada.symbol.input-voltage}", + "hint": "{i18n:scada.symbol.input-voltage-hint}", + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "GET_TIME_SERIES", + "defaultValue": null, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "inputVoltage" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "outputVoltage", + "name": "{i18n:scada.symbol.output-voltage}", + "hint": "{i18n:scada.symbol.output-voltage-hint}", + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "GET_TIME_SERIES", + "defaultValue": null, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "outputVoltage" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "bypassColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#DEDEDE", + "subLabel": "{i18n:scada.symbol.bypass-mode}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "subLabel": "{i18n:scada.symbol.stopped}", + "divider": false, + "disabled": false, + "visible": true + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "disabled": false, + "visible": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "subLabel": "{i18n:scada.symbol.critical}", + "disabled": false, + "visible": true + }, + { + "id": "showInputLabel", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.input-voltage}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "inputLabel", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.input-voltage}", + "type": "text", + "default": "in", + "disabled": false, + "visible": true + }, + { + "id": "inputLabelFont", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.input-voltage}", + "type": "font", + "default": { + "size": 36, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "inputLabelColor", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.input-voltage}", + "type": "color", + "default": "#000", + "disabled": false, + "visible": true + }, + { + "id": "inputValutFont", + "name": "{i18n:scada.symbol.value}", + "group": "{i18n:scada.symbol.input-voltage}", + "type": "font", + "default": { + "size": 44, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "inputValueColor", + "name": "{i18n:scada.symbol.value}", + "group": "{i18n:scada.symbol.input-voltage}", + "type": "color", + "default": "#002878", + "disabled": false, + "visible": true + }, + { + "id": "inputValueBoxBackground", + "name": "{i18n:scada.symbol.value-box-background}", + "group": "{i18n:scada.symbol.input-voltage}", + "type": "color", + "default": "#DEDEDE", + "disabled": false, + "visible": true + }, + { + "id": "showOutputLabel", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.output-voltage}", + "type": "switch", + "default": true, + "disabled": false, + "visible": true + }, + { + "id": "outputLabel", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.output-voltage}", + "type": "text", + "default": "out", + "disabled": false, + "visible": true + }, + { + "id": "outputLabelFont", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.output-voltage}", + "type": "font", + "default": { + "size": 36, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "outputLabelColor", + "name": "{i18n:scada.symbol.label}", + "group": "{i18n:scada.symbol.output-voltage}", + "type": "color", + "default": "#000", + "disabled": false, + "visible": true + }, + { + "id": "outputValueFont", + "name": "{i18n:scada.symbol.value}", + "group": "{i18n:scada.symbol.output-voltage}", + "type": "font", + "default": { + "size": 44, + "sizeUnit": "px", + "family": "Roboto", + "weight": "normal", + "style": "normal" + }, + "disabled": false, + "visible": true + }, + { + "id": "outputValueColor", + "name": "{i18n:scada.symbol.value}", + "group": "{i18n:scada.symbol.output-voltage}", + "type": "color", + "default": "#002878", + "disabled": false, + "visible": true + }, + { + "id": "outputValueBoxBackground", + "name": "{i18n:scada.symbol.value-box-background}", + "group": "{i18n:scada.symbol.output-voltage}", + "type": "color", + "default": "#DEDEDE", + "disabled": false, + "visible": true + } + ] +} +220230inout + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/wind-turbine-cluster-hp.svg b/application/src/main/data/json/system/scada_symbols/wind-turbine-cluster-hp.svg new file mode 100644 index 0000000000..74855dd35a --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/wind-turbine-cluster-hp.svg @@ -0,0 +1,370 @@ +{ + "title": "HP Wind turbine cluster", + "description": "Wind turbine cluster with various states.", + "searchTags": [ + "energy", + "power", + "renewable", + "generation" + ], + "widgetSizeX": 3, + "widgetSizeY": 4, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "required": null, + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null, + "disabled": false, + "visible": true + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "required": null, + "subLabel": "{i18n:scada.symbol.stopped}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null, + "disabled": false, + "visible": true + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "required": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null, + "disabled": false, + "visible": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "required": null, + "subLabel": "{i18n:scada.symbol.critical}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null, + "disabled": false, + "visible": true + } + ] +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/wind-turbine-hp.svg b/application/src/main/data/json/system/scada_symbols/wind-turbine-hp.svg new file mode 100644 index 0000000000..a4282c16e7 --- /dev/null +++ b/application/src/main/data/json/system/scada_symbols/wind-turbine-hp.svg @@ -0,0 +1,360 @@ +{ + "title": "HP Wind turbine", + "description": "Wind turbine with various states.", + "searchTags": [ + "energy", + "power", + "renewable", + "generation" + ], + "widgetSizeX": 3, + "widgetSizeY": 4, + "tags": [ + { + "tag": "background", + "stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});", + "actions": null + }, + { + "tag": "clickArea", + "stateRenderFunction": null, + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'click');" + } + } + }, + { + "tag": "critical", + "stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'criticalClick');" + } + } + }, + { + "tag": "warning", + "stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n", + "actions": { + "click": { + "actionFunction": "ctx.api.callAction(event, 'warningClick');" + } + } + } + ], + "behavior": [ + { + "id": "running", + "name": "{i18n:scada.symbol.running}", + "hint": "{i18n:scada.symbol.running-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.running}", + "defaultGetValueSettings": { + "action": "GET_ATTRIBUTE", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": "SHARED_SCOPE", + "key": "running" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warning", + "name": "{i18n:scada.symbol.warning}", + "hint": "{i18n:scada.symbol.warning-state-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.warning}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "critical", + "name": "{i18n:scada.symbol.critical}", + "hint": "{i18n:scada.symbol.critical-state-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.critical}", + "defaultGetValueSettings": { + "action": "GET_ALARM_STATUS", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, + { + "id": "criticalAnimation", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": "{i18n:scada.symbol.animation}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "click", + "name": "{i18n:scada.symbol.on-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": null, + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + } + ], + "properties": [ + { + "id": "runningColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#FFFFFF", + "required": null, + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null, + "disabled": false, + "visible": true + }, + { + "id": "stoppedColor", + "name": "{i18n:scada.symbol.colors}", + "type": "color", + "default": "#666666", + "required": null, + "subLabel": "{i18n:scada.symbol.stopped}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null, + "disabled": false, + "visible": true + }, + { + "id": "warningColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#FAA405", + "required": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null, + "disabled": false, + "visible": true + }, + { + "id": "criticalColor", + "name": "{i18n:scada.symbol.alarm-colors}", + "type": "color", + "default": "#D12730", + "required": null, + "subLabel": "{i18n:scada.symbol.critical}", + "divider": null, + "fieldSuffix": null, + "disableOnProperty": null, + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null, + "disabled": false, + "visible": true + } + ] +} + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/general_high_performance_scada_symbols.json b/application/src/main/data/json/system/widget_bundles/general_high_performance_scada_symbols.json index 5083384432..33efaacc0c 100644 --- a/application/src/main/data/json/system/widget_bundles/general_high_performance_scada_symbols.json +++ b/application/src/main/data/json/system/widget_bundles/general_high_performance_scada_symbols.json @@ -28,6 +28,10 @@ "hp_left_tee_connector", "hp_top_tee_connector", "hp_drawwork", - "hp_crane" + "hp_crane", + "hp_consumers", + "hp_house", + "hp_apartments", + "hp_manufacture" ] } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/high_performance_scada_energy_system.json b/application/src/main/data/json/system/widget_bundles/high_performance_scada_energy_system.json new file mode 100644 index 0000000000..a06ccc0819 --- /dev/null +++ b/application/src/main/data/json/system/widget_bundles/high_performance_scada_energy_system.json @@ -0,0 +1,48 @@ +{ + "widgetsBundle": { + "alias": "high_performance_scada_energy_system", + "title": "High-performance SCADA energy system", + "image": "tb-image:aHBfc2NhZGFfZW5lcmd5X3N5c3RlbV9idW5kbGVfaW1hZ2UucG5n:IkhpZ2gtcGVyZm9ybWFuY2UgU0NBREEgZW5lcmd5IHN5c3RlbSIgc3lzdGVtIGJ1bmRsZSBpbWFnZQ==:SU1BR0U=;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAllBMVEXe3t7b29vf398AAADf39/e3t7e3t7////Gxsbj4+OpqanFxcW5ubmtra2xsbGVlZWlpaWhoaHS0tLx8fG2trbU1NTBwcGvr6+MjIybm5twcHC+vr6jo6OYmJizs7Orq6vExMSZmZnCwsKdnZ2ampqRkZE3Nze7u7t+fn6EhIRjY2NVVVVsbGx5eXmJiYlEREQvLy8aGhprAy3xAAAABnRSTlPvIK8Av79l/pT7AAAJyElEQVR42uzVXW/aMBiGYWin57VjmyRmNGFLKOWjQACt/P8/t7yJQVMPrE62KkBcR88ZuokTD56HPwa4dU/D58Hw9jNabcYT7sIdHKve4F5C8Ai5No+Qa/MIuTaPkGvzCLk2QSHKIB6rFAIEhayniCc9fVx2c2ou++OUwi88ZGEtYrHbfZOf9/59r9w2f963Fj7hIUaOK0SSWyKSS8s7461rsOmk3TMNj/CQKVGBOJSiTsJNkpjcACjHxOYWHsEhCyJhEYWW1FkDeKNeDcD0c2LgERpi+McrxFFqIYQeaQCqnshWrQAUL7zFGh7BIVNq5YjDJm1Jnfd7lySrnQLT23aX8AgPWbjTG0VFTHKJyohlXLKZ8xy/wiMwxB3rsUEwd0yZ+ecdyQCs+yk1PAJDMuokiMFmxMZl/9W6PJ2EOlrBIzAkp84IUahym6yOG7D0WCarXQE2Oq6Scm3gERoyJxbrA5wJfiCV4l3zljVPu5R8IW7gERZy+fKPC0SgJu4e+XS0Ss+/FSkkI2eKQJ8vxOx7L8RXcjaI4W3GF2KhAKhU8CXYbTHjrZfwCAz5Tc4LohA/pVy7nbZRqds7IVYaHqEhc3ImFgGKkZM1TVO6XR4Oh9rtX+2uRo4BYodYQY7UCCDofwggdoiRdJYDuOEQYuFXYjEqk68qqwKIHZJHCoGQ9FVSgcUNSe8lxNBFBXarIVrSmQG71ZC/1Jpdc6sgEIbv3AXRIB9VE2OStmN6mpnT8/9/3RlSbGTxzmFC9yYTfV14wrIsRLQk/f4ikHDhM+ANVSD6BSB/VLg39SbDJKCyBxFst1o0tkEOsEP2ID20q/nXEFH2IK+Aai1tVVSUOYgrrnYrsx0VEQ2Zg3A6HVq/rlNRkzlI7U/jouMgutsyKmuQEulpHMYHdKW9X8kapIl//hHcIK2Isgbp4zWDRznr9C3KGuQAzkSQorSf60R0zhnEb9G1WF77K7AvYxETm0HYJ0sEUuko2V6lu/Zax6JhM8j+a58IZIjqkav13uuoaum3gtgJJkwDYqJ6ZARvZ0VFm0Nr4oC3NCBv3teBhJGzHS2I240g3MXVjScBEXTzcVm0S0WHbSBsYsAY+6fTgJDJLtOBdB2wzn0kD62yMYdew2z94dSIbaFFB2T/9Ql6YglA5kh6UxUf7X0cwNu9J/b1VCk5izaB7J1nt470t2TpVwup5pGwc/q1P0Q73ymzBYRPrqF7KzdMMEcYAHK79CpHDaDNUcHDEHFraE0ffLaPKUGJYizqwGldnAQKcyyuNrjO0YotIN3+YX2CEbno0OelKCofRgMLb/WiyLZolBYijqKZy5GakbudyhOkbHXM4Yt2UayQgCxzBMER1jiK2oOskZzb/EDqgvqTi7Kx8SJLRcfcQI60oWLwd16W5Qi+RCR5gYQcwYuSPKgiSxkNXE4glEO3D2cYvi6gBNXKfEAoB17oDv1loZaGkuQCQji0sG/0Pyu+BGFIHjjmASKDNrTr+VmRHmB4GoSchTM+B5CWLSisQOKrYvQwnvtiSy9n/PNBSvUYC/7hO4enWswgofNyd+Je33Hxw2KvzwYpR991I6wG9L44ADNVXYYgqhGSwY8I70/5b+fyuSBKAjDRCaPBmX4HZ8a3ik1dDt/OK7VrEVZEWvB3YQFG9VQQ+b+9c1tuGwTCcI+wIJCEhK26suW2aafpRfv+j9ehIgtZuQk20QRn8t9kRP7Y+5nDamUUDfbzr1AzWT2oDTQ65G+YQfiPYPqmBxD3TU0jh9tnHlo74CgGzHLRH5gKqUJ6xGBSTM6mLxwl1NMNrc1FPdLxexzsZq6iFONegCDBdJxPWgBNzvDgwrVnG5GqDavPBemkIBxM+ewHd4Rq/lEtTYREHLvn2njmJnJQB/Nq64sPtcWhFf34AswXjcd/JkvqrGcAoSey3xR+iauiX4sdjrBwTjwG00RIngGk2vJIdcUwRuMNNecCZDTC9pFJn/w0/kfSwjlqk0Eoh5Cdb67ji7o7AA4IEkwu+AFNgpCsKQpCxsQ0YLvDq9FkcdUCrtoKTTEtM1MGSS4Ijoj5KiJDWceFpmiyyz4Eacm2pzrq3WlVEgrSIQeM97deWayiPGzlffFkBm8Kqs3msRm/AgherIaBvOsco41NNk6I+46aUFVtgKNpJVGQnYOo5bB8xw5wZZWzyUfXyhF6nz0ATm986IysnWnHVhIF+dMcW92xpXxhC7EJws0KejoOf5xJeNMJdXq6GRqWqfydDyDIWRvAcgOdciZYcyrkg2hBzqMdmZOKTb0zFQ5CN2doDwJdbLoSkF5HDf2ySDBzzchWUz6IX3JjECzbqKlskNpvckA1WAJSk2HrKR/kK90c5IKmw61N3BxUGQVBzdmTSis4XApiKUjLvczS9JgU+X4rHQE/RMgDORAQMtwkmlJeDCVU6HhtHtbXLhPkBrNfFAztgGPiBwycT7XXPl7nJH9UQw4IjgZJyHzcxJQCgn+lRQQCKRe38kA0TePqBIjhTscUkPoUCKSA6DyQAUEiMjrcRm9KAWmn7SwAsFsvmSA7HjJAfIyGkDntqEmngPzu+cXqf2eA1HRL2ci9FF3JhhQQyTMkM0C2y4TopalpLBrEcqeJBO00UFN7BSBb0kA6AGZT0SDw34TYLkwlg3RAJzbQ/zCAaUCWDOJvBAVCFncAVr9QNIjAcjAiIx2gPEhVMIgWpBxUgl7qx+pXFA1CT7h1jyBVMF0NSK/jBhq38Q26YJA9TeMmAqGmkkFqWg7uA4imJlMwyFdaDtYcpWn1uy8YxD5w20WzMF0ByCFuoHFLNKWCbCRKcREdrAZyQ9O4XILg/SNtMogA1Ib30cFqIIqm8SNHHRamgoeWpuWg4ihLl+RjwSADBTEcJWn1q9LnyGfUrTtArQWCMZq4gcY9XlFCHEMDStEleSgYZEtXqImjDDWNySC9QPXxgVg9j0xx0DRue/byC59Qkm8+Ba0Oso3zCI0b7kwFDy2gSy1w1LQwlQuCFbqKG0gHAKyYEDfKKlB9Jkio0CEOmsQNAk1PDvJ9RpAiF0TgjtWogcStEKR6ahDZI1AeiBakHNQxSEfKeJEAouQZ+o7939+6Y3U5CP26T5N9xdiWBGL5eUKOsDtXZYL0OjTQuA0tfk8LnelC8DAk9WUge1rXNjGIWpoeVqXhLH2KmMBJmeoCkP1u90Pe6dfOCRtCGzG17FE1QogmeYrSpf0CkJZfoG0CCNb7yVUE/1JdN0jV4s3nhYIYlkiigHMx/mQ5IKa5QIYlvTBLVqUbxU7p9Rk9heoVpDS9gpSmV5DS9ApSml7Mg3XffGAvQu9ezkO037+9/sdov/n49v1fKIFpFaO3gooAAAAASUVORK5CYII=", + "scada": true, + "description": "Bundle with high-performance SCADA symbols for energy system", + "order": 9410, + "name": "High-performance SCADA energy system" + }, + "widgetTypeFqns": [ + "hp_solar_panel", + "hp_stand_solar_panel", + "hp_wind_turbine", + "hp_wind_turbine_cluster", + "hp_fuel_generator", + "hp_industrial_fuel_generator", + "hp_circuit_breaker2", + "hp_horizontal_circuit_breaker", + "hp_voltage_relay", + "hp_3_phase_voltage_relay", + "hp_voltage_stabilizer", + "hp_energy_meter", + "hp_two_rate_energy_meter", + "hp_three_rate_energy_meter", + "hp_four_rate_energy_meter", + "hp_electrical_distribution_board", + "hp_power_socket", + "hp_single_key_switch", + "hp_two_key_switch", + "hp_top_light_bulb", + "hp_bottom_light_bulb", + "hp_battery", + "hp_inverter", + "hp_large_inverter", + "hp_horizontal_energy_systems_controller", + "hp_vertical_energy_systems_controller", + "hp_small_power_transformer", + "hp_power_transformer", + "hp_low_voltage_transformer_tower", + "hp_low_voltage_tower", + "hp_high_voltage_tower", + "hp_consumers", + "hp_house", + "hp_apartments", + "hp_manufacture" + ] +} \ No newline at end of file diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index a16625bdaa..326f4c35d7 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3250,8 +3250,11 @@ "rotation-animation-speed-hint": "Double value indicating rotation animation speed. 1 - normal speed, 0 - no animation, < 1 - slower animation, > 1 - faster animation.", "on-click": "On click", "on-click-hint": "Action invoked when user clicks on the component.", + "connectors-positions": "Connectors positions", + "right-connector": "Right connector", "right-top-connector": "Right top connector", "right-bottom-connector": "Right bottom connector", + "left-connector": "Left connector", "left-top-connector": "Left top connector", "left-bottom-connector": "Left bottom connector", "top-left-connector": "Top left connector", @@ -3363,6 +3366,63 @@ "low-critical-state-hint": "Double value indicates a low critical range up to the min value scale.", "filter-color": "Filter color", "colors": "Colors", + "indicator-colors": "Indicator colors", + "enabled": "Enabled", + "disabled": "Disabled", + "on": "ON", + "off": "OFF", + "on-off-state": "On/Off state", + "on-off-state-hint": "Indicates whether the component is in the On or Off state.", + "on-update-state": "On update state", + "on-update-state-hint": "Action invoked when the user clicks to update the state to On.", + "off-update-state": "Off update state", + "off-update-state-hint": "Action invoked when the user clicks to update the state to Off.", + "voltage": "Voltage", + "input-voltage": "Input voltage", + "input-voltage-hint": "Double value indicates input voltage value.", + "output-voltage": "Output voltage", + "output-voltage-hint": "Double value indicates output voltage value.", + "first-phase-voltage": "First phase voltage", + "second-phase-voltage": "Second phase voltage", + "third-phase-voltage": "Third phase voltage", + "phase-voltage-hint": "Double value indicates voltage value for current phase", + "voltage-hint": "Double value indicates current voltage", + "current-voltage-color": "Current voltage color", + "phase-indicator-color": "Phase indicator color", + "measured": "Measured", + "measured-hint": "Double value indicates energy usage in kilowatt-hours", + "day-rate": "Day rate", + "night-rate": "Night rate", + "off-peak-rate": "Off-peak rate", + "peak-rate": "Peak rate", + "export-rate": "Export rate", + "operating-mode": "Operating mode", + "bypass-mode": "Bypass", + "operating-mode-hint": "Integer value indication the current operating mode (0 - OFF, 1 - ON, 2 - BYPASS)", + "connected": "Connected", + "connected-hint": "Indicates whether component is in connected state.", + "disconnected": "Disconnected", + "indicator": "Indicator", + "operation-mode": "Operation mode", + "operation-mode-hint": "Indicates whether inverter is in Mains or Inverter mode.", + "operation-mode-indicators-color": "Operation mode indicators color", + "mains-on-mode": "Mains on", + "inverter-on-mode": "Inverter on", + "charging-mode": "Charging mode", + "charging-mode-hint": "Integer value indication the current charging mode (1 - Bulk, 2 - Absorption, 3 - Float)", + "charging-mode-indicators-color": "Charging mode indicators color", + "inverter-faults": "Faults", + "inverter-fault-indicators-color": "Fault indicators color", + "overload-fault": "Overload", + "overload-fault-hint": "Indicates whether inverter is in overload condition.", + "low-battery-fault": "Low battery", + "low-battery-fault-hint": "Indicates whether battery is excessively discharged.", + "temperature-fault": "Temperature", + "temperature-fault-hint": "Indicates whether high temperature in an inverter.", + "triangle": "Triangle", + "socket": "Socket", + "left-button": "Left button", + "right-button": "Right button", "alarm-colors": "Alarm colors", "hook-color": "Hook color" } From f2b5aa9549f4541c831f269649ac9269d9fdea9d Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 27 Jan 2025 19:01:43 +0200 Subject: [PATCH 098/108] UI: Fixed validation issues for URLs on the mobile page configuration, including Unicode symbols --- .../bundes/layout/custom-mobile-page.component.ts | 11 ++++++++--- ui-ngx/src/app/shared/models/mobile-app.models.ts | 2 ++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.ts index 3075ae772b..e9576f313a 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.ts @@ -24,7 +24,12 @@ import { Validator, Validators } from '@angular/forms'; -import { CustomMobilePage, MobilePageType, mobilePageTypeTranslations } from '@shared/models/mobile-app.models'; +import { + CustomMobilePage, + MobilePageType, + mobilePageTypeTranslations, + WEB_URL_REGEX +} from '@shared/models/mobile-app.models'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { getCurrentAuthUser } from '@core/auth/auth.selectors'; import { Authority } from '@shared/models/authority.enum'; @@ -63,8 +68,8 @@ export class CustomMobilePageComponent implements ControlValueAccessor, Validato label: ['', [Validators.required, Validators.pattern(/\S/)]], type: [MobilePageType.DASHBOARD], dashboardId: this.fb.control(null, Validators.required), - url: [{value:'', disabled: true}, [Validators.required, Validators.pattern(/^(https?:\/\/)?(localhost|([\w\-]+\.)+[\w\-]+)(:\d+)?(\/[\w\-._~:\/?#[\]@!$&'()*+,;=%]*)?$/)]], - path: [{value:'', disabled: true}, [Validators.required, Validators.pattern(/^(\/[\w\-._~:\/?#[\]@!$&'()*+,;=%]*)?$/)]] + url: [{value:'', disabled: true}, [Validators.required, Validators.pattern(WEB_URL_REGEX)]], + path: [{value:'', disabled: true}, [Validators.required, Validators.pattern(/^(\/[\w\-._~:/?#[\]@!$&'()*+,;=%]*)?$/)]] }); private propagateChange = (_val: any) => {}; diff --git a/ui-ngx/src/app/shared/models/mobile-app.models.ts b/ui-ngx/src/app/shared/models/mobile-app.models.ts index acf609a637..7286910adc 100644 --- a/ui-ngx/src/app/shared/models/mobile-app.models.ts +++ b/ui-ngx/src/app/shared/models/mobile-app.models.ts @@ -21,6 +21,8 @@ import { OAuth2ClientInfo, PlatformType } from '@shared/models/oauth2.models'; import { MobileAppBundleId } from '@shared/models/id/mobile-app-bundle-id'; import { deepClone, isNotEmptyStr } from '@core/utils'; +export const WEB_URL_REGEX = /^(https?:\/\/)?(localhost|([\p{L}\p{M}\w-]+\.)+[\p{L}\p{M}\w-]+)(:\d+)?(\/[\w\-._~:/?#[\]@!$&'()*+,;=%\p{L}\p{N}]*)?$/u; + export interface QrCodeSettings extends HasTenantId { useDefaultApp: boolean; mobileAppBundleId: MobileAppBundleId From d0d8bfbed1a66efe137581f89874768fc2d7e6d9 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Wed, 29 Jan 2025 09:48:10 +0200 Subject: [PATCH 099/108] UI: Radio button for multiple input widget --- .../lib/multiple-input-widget.component.html | 22 ++ .../lib/multiple-input-widget.component.scss | 2 +- .../lib/multiple-input-widget.component.ts | 14 +- ...ple-attributes-key-settings.component.html | 254 ++++++++++-------- ...tiple-attributes-key-settings.component.ts | 16 +- .../assets/locale/locale.constant-en_US.json | 12 +- 6 files changed, 208 insertions(+), 112 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.html index 51d4ec22b1..3e1ca011fd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.html @@ -138,6 +138,28 @@ {{ getErrorMessageText(key.settings, 'required') }}
    + +
    + {{key.label}} + + + + + + + {{ getCustomTranslationText(option.label ? option.label : option.value) }} + + + +
    diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.scss index 4c8ffea030..b5e83a4925 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.scss @@ -75,7 +75,7 @@ :host ::ng-deep { .tb-multiple-input { - .mat-mdc-slide-toggle, .mat-mdc-checkbox { + .mat-mdc-slide-toggle, .mat-mdc-checkbox, .mat-mdc-radio-button { .mdc-form-field { width: 100%; & > label { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts index 7a80f4904a..94b33eb9d0 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts @@ -54,7 +54,7 @@ type FieldAlignment = 'row' | 'column'; type MultipleInputWidgetDataKeyType = 'server' | 'shared' | 'timeseries'; export type MultipleInputWidgetDataKeyValueType = 'string' | 'double' | 'integer' | 'JSON' | 'booleanCheckbox' | 'booleanSwitch' | - 'dateTime' | 'date' | 'time' | 'select' | 'color'; + 'dateTime' | 'date' | 'time' | 'select' | 'radio' | 'color'; export type MultipleInputWidgetDataKeyEditableType = 'editable' | 'disabled' | 'readonly'; type ConvertGetValueFunction = (value: any, ctx: WidgetContext) => any; @@ -86,6 +86,9 @@ interface MultipleInputWidgetDataKeySettings { dataKeyValueType: MultipleInputWidgetDataKeyValueType; slideToggleLabelPosition?: 'after' | 'before'; selectOptions: MultipleInputWidgetSelectOption[]; + radioColor: string; + radioColumns: number; + radioLabelPosition?: 'after' | 'before'; required: boolean; isEditable: MultipleInputWidgetDataKeyEditableType; disabledOnDataKey: string; @@ -300,7 +303,7 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni // For backward compatibility - if (dataKey.settings.dataKeyValueType === 'select') { + if (dataKey.settings.dataKeyValueType === 'select' || dataKey.settings.dataKeyValueType === 'radio') { dataKey.settings.selectOptions.forEach((option) => { if (option.value.toLowerCase() === 'null') { option.value = null; @@ -444,6 +447,7 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni } break; case 'select': + case 'radio': value = keyValue !== null ? keyValue.toString() : null; break; case 'JSON': @@ -566,6 +570,12 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni return this.getTranslatedErrorText(errorMessage, defaultMessage, messageValues); } + public radioButtonSelectedColor(radioColor: string) { + if (isDefinedAndNotNull(radioColor)) { + return `--mdc-radio-selected-icon-color: ${radioColor}; --mdc-radio-selected-focus-icon-color: ${radioColor}; --mdc-radio-selected-hover-icon-color: ${radioColor}; --mdc-radio-selected-pressed-icon-color: ${radioColor}; --mat-radio-checked-ripple-color: ${radioColor};` + } + } + public getTranslatedErrorText(errorMessage: string, defaultMessage: string, messageValues?: object): string { let messageText; if (errorMessage && errorMessage.length) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.html index 93c53c27ec..37a4dee4d5 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.html @@ -21,108 +21,113 @@
    widgets.input-widgets.general-settings -
    - - widgets.input-widgets.datakey-type - - - {{ 'widgets.input-widgets.datakey-type-server' | translate }} - - - {{ 'widgets.input-widgets.datakey-type-shared' | translate }} - - - {{ 'widgets.input-widgets.datakey-type-timeseries' | translate }} - - - - - widgets.input-widgets.datakey-value-type - - - {{ 'widgets.input-widgets.datakey-value-type-string' | translate }} - - - {{ 'widgets.input-widgets.datakey-value-type-double' | translate }} - - - {{ 'widgets.input-widgets.datakey-value-type-integer' | translate }} - - - {{ 'widgets.input-widgets.datakey-value-type-boolean-checkbox' | translate }} - - - {{ 'widgets.input-widgets.datakey-value-type-boolean-switch' | translate }} - - - {{ 'widgets.input-widgets.datakey-value-type-date-time' | translate }} - - - {{ 'widgets.input-widgets.datakey-value-type-date' | translate }} - - - {{ 'widgets.input-widgets.datakey-value-type-time' | translate }} - - - {{ 'widgets.input-widgets.datakey-value-type-select' | translate }} - - - {{ 'widgets.input-widgets.datakey-value-type-json' | translate }} - - - {{ 'widgets.input-widgets.datakey-value-type-color' | translate }} - - - -
    - - {{ 'widgets.input-widgets.value-is-required' | translate }} - -
    - - widgets.input-widgets.ability-to-edit-attribute - - - {{ 'widgets.input-widgets.ability-to-edit-attribute-editable' | translate }} - - - {{ 'widgets.input-widgets.ability-to-edit-attribute-disabled' | translate }} - - - {{ 'widgets.input-widgets.ability-to-edit-attribute-readonly' | translate }} - - - - - widgets.input-widgets.disable-on-datakey-name - - -
    -
    - - widgets.input-widgets.field-appearance - - - {{ 'widgets.input-widgets.appearance-fill' | translate }} - - - {{ 'widgets.input-widgets.appearance-outline' | translate }} - - - - - widgets.input-widgets.subscript-sizing - - - {{ 'widgets.input-widgets.subscript-sizing-fixed' | translate }} - - - {{ 'widgets.input-widgets.subscript-sizing-dynamic' | translate }} - - - -
    +
    +
    + + widgets.input-widgets.datakey-type + + + {{ 'widgets.input-widgets.datakey-type-server' | translate }} + + + {{ 'widgets.input-widgets.datakey-type-shared' | translate }} + + + {{ 'widgets.input-widgets.datakey-type-timeseries' | translate }} + + + + + widgets.input-widgets.datakey-value-type + + + {{ 'widgets.input-widgets.datakey-value-type-string' | translate }} + + + {{ 'widgets.input-widgets.datakey-value-type-double' | translate }} + + + {{ 'widgets.input-widgets.datakey-value-type-integer' | translate }} + + + {{ 'widgets.input-widgets.datakey-value-type-boolean-checkbox' | translate }} + + + {{ 'widgets.input-widgets.datakey-value-type-boolean-switch' | translate }} + + + {{ 'widgets.input-widgets.datakey-value-type-date-time' | translate }} + + + {{ 'widgets.input-widgets.datakey-value-type-date' | translate }} + + + {{ 'widgets.input-widgets.datakey-value-type-time' | translate }} + + + {{ 'widgets.input-widgets.datakey-value-type-select' | translate }} + + + {{ 'widgets.input-widgets.datakey-value-type-radio' | translate }} + + + {{ 'widgets.input-widgets.datakey-value-type-json' | translate }} + + + {{ 'widgets.input-widgets.datakey-value-type-color' | translate }} + + + +
    + + {{ 'widgets.input-widgets.value-is-required' | translate }} + +
    + + widgets.input-widgets.ability-to-edit-attribute + + + {{ 'widgets.input-widgets.ability-to-edit-attribute-editable' | translate }} + + + {{ 'widgets.input-widgets.ability-to-edit-attribute-disabled' | translate }} + + + {{ 'widgets.input-widgets.ability-to-edit-attribute-readonly' | translate }} + + + + + widgets.input-widgets.disable-on-datakey-name + + +
    +
    + + widgets.input-widgets.field-appearance + + + {{ 'widgets.input-widgets.appearance-fill' | translate }} + + + {{ 'widgets.input-widgets.appearance-outline' | translate }} + + + + + widgets.input-widgets.subscript-sizing + + + {{ 'widgets.input-widgets.subscript-sizing-fixed' | translate }} + + + {{ 'widgets.input-widgets.subscript-sizing-dynamic' | translate }} + + + +
    +
    @@ -140,8 +145,10 @@
    - widgets.input-widgets.select-options + (updateMultipleAttributesKeySettingsForm.get('dataKeyValueType').value !== 'select' && updateMultipleAttributesKeySettingsForm.get('dataKeyValueType').value !== 'radio')" class="fields-group"> + + {{ (updateMultipleAttributesKeySettingsForm.get('dataKeyValueType').value === 'select' ? 'widgets.input-widgets.select-options' : 'widgets.input-widgets.radio-options') | translate }} +
    @@ -155,18 +162,51 @@
    - widgets.input-widgets.no-select-options + + {{ (updateMultipleAttributesKeySettingsForm.get('dataKeyValueType').value === 'select' ? 'widgets.input-widgets.no-select-options' : 'widgets.input-widgets.no-radio-options') | translate }} +
    +
    +
    widgets.input-widgets.radio-button-settings
    +
    +
    {{ 'widgets.input-widgets.color' | translate }}
    + + +
    +
    +
    {{ 'widgets.input-widgets.columns' | translate }}
    + + + +
    +
    +
    {{ 'widgets.input-widgets.radio-label-position' | translate }}
    + + + + {{ 'widgets.input-widgets.radio-label-position-after' | translate }} + + + {{ 'widgets.input-widgets.radio-label-position-before' | translate }} + + + +
    +
    diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.ts index 24e0ac7072..378c2fdeab 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.ts @@ -60,6 +60,9 @@ export class UpdateMultipleAttributesKeySettingsComponent extends WidgetSettings slideToggleLabelPosition: 'after', selectOptions: [], + radioColor: null, + radioColumns: 1, + radioLabelPosition: 'after', step: 1, minValue: null, maxValue: null, @@ -104,10 +107,16 @@ export class UpdateMultipleAttributesKeySettingsComponent extends WidgetSettings slideToggleLabelPosition: [settings.slideToggleLabelPosition, []], - // Select options + // Select/Radio options selectOptions: this.prepareSelectOptionsFormArray(settings.selectOptions), + // Radio settings + + radioColor: [settings.radioColor, []], + radioColumns: [settings.radioColumns, []], + radioLabelPosition: [settings.radioLabelPosition, []], + // Numeric field settings step: [settings.step, [Validators.min(0)]], @@ -183,6 +192,11 @@ export class UpdateMultipleAttributesKeySettingsComponent extends WidgetSettings this.updateMultipleAttributesKeySettingsForm.get('slideToggleLabelPosition').enable({emitEvent: false}); } else if (dataKeyValueType === 'select') { this.updateMultipleAttributesKeySettingsForm.get('selectOptions').enable({emitEvent: false}); + } else if (dataKeyValueType === 'radio') { + this.updateMultipleAttributesKeySettingsForm.get('selectOptions').enable({emitEvent: false}); + this.updateMultipleAttributesKeySettingsForm.get('radioColor').enable({emitEvent: false}); + this.updateMultipleAttributesKeySettingsForm.get('radioColumns').enable({emitEvent: false}); + this.updateMultipleAttributesKeySettingsForm.get('radioLabelPosition').enable({emitEvent: false}); } else if (dataKeyValueType === 'integer' || dataKeyValueType === 'double') { this.updateMultipleAttributesKeySettingsForm.get('step').enable({emitEvent: false}); this.updateMultipleAttributesKeySettingsForm.get('minValue').enable({emitEvent: false}); diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index a16625bdaa..305a831f9a 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -7340,6 +7340,7 @@ "datakey-value-type-date": "Date", "datakey-value-type-time": "Time", "datakey-value-type-select": "Select", + "datakey-value-type-radio": "Radio", "datakey-value-type-color": "Color", "value-is-required": "Value is required", "ability-to-edit-attribute": "Ability to edit attribute", @@ -7380,7 +7381,16 @@ "set-value-function": "setValue function", "json-invalid": "JSON value has an invalid format", "title": "Title", - "cancel-button-label": "'Cancel' button label" + "cancel-button-label": "'Cancel' button label", + "radio-button-settings": "Radio button settings", + "color": "Color", + "columns": "Columns", + "radio-options": "Radio options", + "no-radio-options-configured": "No radio options configured", + "add-radio-option": "Add radio option", + "radio-label-position": "Label position", + "radio-label-position-before": "Before", + "radio-label-position-after": "After" }, "invalid-qr-code-text": "Invalid input text for QR code. Input should have a string type", "qr-code": { From 2514c0372c875ddded523f14ce1f60cbdcabaf65 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Wed, 29 Jan 2025 10:46:08 +0200 Subject: [PATCH 100/108] UI: Fixed translate --- ui-ngx/src/assets/locale/locale.constant-en_US.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 305a831f9a..fa6e9bb052 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -7386,7 +7386,7 @@ "color": "Color", "columns": "Columns", "radio-options": "Radio options", - "no-radio-options-configured": "No radio options configured", + "no-radio-options": "No radio options configured", "add-radio-option": "Add radio option", "radio-label-position": "Label position", "radio-label-position-before": "Before", From 36ec215d67676599e49886ec73347da38d8cadbb Mon Sep 17 00:00:00 2001 From: deaflynx Date: Wed, 29 Jan 2025 11:29:42 +0200 Subject: [PATCH 101/108] UI: Entity data subscription cmds prepare keys without duplicates. --- ui-ngx/src/app/core/api/entity-data-subscription.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/core/api/entity-data-subscription.ts b/ui-ngx/src/app/core/api/entity-data-subscription.ts index 46cd2af7ab..daeb6b8faf 100644 --- a/ui-ngx/src/app/core/api/entity-data-subscription.ts +++ b/ui-ngx/src/app/core/api/entity-data-subscription.ts @@ -685,7 +685,7 @@ export class EntityDataSubscription { if (this.tsFields.length > 0) { if (this.history) { cmd.historyCmd = { - keys: this.tsFields.map(key => key.key), + keys: [... new Set(this.tsFields.map(key => key.key))], startTs: this.subsTw.fixedWindow.startTimeMs, endTs: this.subsTw.fixedWindow.endTimeMs, interval: 0, @@ -702,7 +702,7 @@ export class EntityDataSubscription { } } else { cmd.tsCmd = { - keys: this.tsFields.map(key => key.key), + keys: [... new Set(this.tsFields.map(key => key.key))], startTs: this.subsTw.startTs, timeWindow: this.subsTw.aggregation.timeWindow, interval: 0, From 5d08ac2dab8d908f90254a2abf502db5002d9d97 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 29 Jan 2025 17:12:58 +0200 Subject: [PATCH 102/108] UI: Fixed error in trip animation widget when path decorator setting is enabled in production mode --- .../src/app/modules/home/components/widget/lib/maps/polyline.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/polyline.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/polyline.ts index 347782d210..46466985a3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/polyline.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/polyline.ts @@ -15,8 +15,8 @@ /// // @ts-ignore -import L, { PolylineDecorator, PolylineDecoratorOptions, Symbol } from 'leaflet'; import 'leaflet-polylinedecorator'; +import L, { PolylineDecorator, PolylineDecoratorOptions, Symbol } from 'leaflet'; import { WidgetPolylineSettings } from './map-models'; import { functionValueCalculator } from '@home/components/widget/lib/maps/common-maps-utils'; From 9004bc8d6d4d0ac5ce0b448d467d96029d43c3fd Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 30 Jan 2025 13:45:42 +0200 Subject: [PATCH 103/108] UI: Add auto extract widget settings component from module --- .../src/app/core/http/rule-chain.service.ts | 3 +- ui-ngx/src/app/core/http/widget.service.ts | 42 ++++++- .../app/core/services/resources.service.ts | 5 +- .../basic/basic-widget-config.module.ts | 50 +------- .../config/widget-settings.component.ts | 3 +- .../lib/settings/widget-settings.module.ts | 116 ++---------------- .../widget/widget-component.service.ts | 13 +- .../widget/widget-config.component.ts | 5 +- 8 files changed, 69 insertions(+), 168 deletions(-) diff --git a/ui-ngx/src/app/core/http/rule-chain.service.ts b/ui-ngx/src/app/core/http/rule-chain.service.ts index 749fb39e72..0e857c4570 100644 --- a/ui-ngx/src/app/core/http/rule-chain.service.ts +++ b/ui-ngx/src/app/core/http/rule-chain.service.ts @@ -33,6 +33,7 @@ import { LinkLabel, RuleNodeComponentDescriptor, RuleNodeConfiguration, + RuleNodeConfigurationComponent, ScriptLanguage, TestScriptInputParams, TestScriptResult @@ -181,7 +182,7 @@ export class RuleChainService { } public registerSystemRuleNodeConfigModule(module: any) { - Object.assign(this.ruleNodeConfigComponents, this.resourcesService.extractComponentsFromModule(module, true)); + Object.assign(this.ruleNodeConfigComponents, this.resourcesService.extractComponentsFromModule(module, RuleNodeConfigurationComponent, true)); } private loadRuleNodeComponents(ruleChainType: RuleChainType, config?: RequestConfig): Observable> { diff --git a/ui-ngx/src/app/core/http/widget.service.ts b/ui-ngx/src/app/core/http/widget.service.ts index ebf390866c..030d53de6f 100644 --- a/ui-ngx/src/app/core/http/widget.service.ts +++ b/ui-ngx/src/app/core/http/widget.service.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Injectable } from '@angular/core'; +import { Injectable, Type } from '@angular/core'; import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; import { Observable, of, ReplaySubject } from 'rxjs'; import { HttpClient } from '@angular/common/http'; @@ -24,7 +24,10 @@ import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; import { BaseWidgetType, DeprecatedFilter, - fullWidgetTypeFqn, migrateWidgetTypeToDynamicForms, + fullWidgetTypeFqn, + IWidgetSettingsComponent, + migrateWidgetTypeToDynamicForms, + WidgetSettingsComponent, WidgetType, widgetType, WidgetTypeDetails, @@ -36,6 +39,11 @@ import { filter, map, mergeMap, tap } from 'rxjs/operators'; import { WidgetTypeId } from '@shared/models/id/widget-type-id'; import { NULL_UUID } from '@shared/models/id/has-uuid'; import { ActivationEnd, Router } from '@angular/router'; +import { + BasicWidgetConfigComponent, + IBasicWidgetConfigComponent +} from '@home/components/widget/config/widget-config.component.models'; +import { ResourcesService } from '@core/services/resources.service'; @Injectable({ providedIn: 'root' @@ -50,9 +58,13 @@ export class WidgetService { private loadWidgetsBundleCacheSubject: ReplaySubject; + private basicWidgetSettingsComponentsMap: { [key: string]: Type } = {}; + private widgetSettingsComponentsMap: { [key: string]: Type } = {}; + constructor( private http: HttpClient, - private router: Router + private router: Router, + private resourcesService: ResourcesService, ) { this.router.events.pipe(filter(event => event instanceof ActivationEnd)).subscribe( () => { @@ -287,6 +299,30 @@ export class WidgetService { this.widgetsInfoInMemoryCache.set(widgetInfo.fullFqn, widgetInfo); } + public registerBasicWidgetConfigComponents(module: any) { + Object.assign(this.basicWidgetSettingsComponentsMap, this.resourcesService.extractComponentsFromModule(module, BasicWidgetConfigComponent)); + } + + public getBasicWidgetSettingsComponentBySelector(selector: string): Type { + return this.basicWidgetSettingsComponentsMap[selector]; + } + + public putBasicWidgetSettingsComponentToMap(selector: string, compType: Type) { + this.basicWidgetSettingsComponentsMap[selector] = compType; + } + + public registerWidgetSettingsComponents(module: any) { + Object.assign(this.widgetSettingsComponentsMap, this.resourcesService.extractComponentsFromModule(module, WidgetSettingsComponent)); + } + + public getWidgetSettingsComponentTypeBySelector(selector: string): Type { + return this.widgetSettingsComponentsMap[selector]; + } + + public putWidgetSettingsComponentToMap(selector: string, compType: Type) { + this.widgetSettingsComponentsMap[selector] = compType; + } + private widgetTypeUpdated(updatedWidgetType: BaseWidgetType): void { this.deleteWidgetInfoFromCache(fullWidgetTypeFqn(updatedWidgetType)); } diff --git a/ui-ngx/src/app/core/services/resources.service.ts b/ui-ngx/src/app/core/services/resources.service.ts index 72cea30a1a..e2ee21c738 100644 --- a/ui-ngx/src/app/core/services/resources.service.ts +++ b/ui-ngx/src/app/core/services/resources.service.ts @@ -265,12 +265,15 @@ export class ResourcesService { ); } - public extractComponentsFromModule(module: any, isCamelCaseSelector = false): ComponentsSelectorMap { + public extractComponentsFromModule(module: any, instanceFilter?: any, isCamelCaseSelector = false): ComponentsSelectorMap { const modulesWithComponents = this.extractModulesWithComponents(module); const componentMap: ComponentsSelectorMap = {}; const processComponents = (components: Array<ɵComponentDef>) => { components.forEach(item => { + if (instanceFilter && !(item.type.prototype instanceof instanceFilter)) { + return; + } let selector = extractSelectorFromComponent(item); if (isCamelCaseSelector) { selector = camelCase(selector); diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts index 800deec7e5..24cb04cca6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts @@ -14,10 +14,10 @@ /// limitations under the License. /// -import { NgModule, Type } from '@angular/core'; +import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { SharedModule } from '@shared/shared.module'; -import { IBasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; +import { WidgetService } from '@core/http/widget.service'; import { WidgetConfigComponentsModule } from '@home/components/widget/config/widget-config-components.module'; import { SimpleCardBasicConfigComponent @@ -251,47 +251,7 @@ import { ] }) export class BasicWidgetConfigModule { + constructor(private widgetService: WidgetService) { + this.widgetService.registerBasicWidgetConfigComponents(this.constructor) + } } - -export const basicWidgetConfigComponentsMap: {[key: string]: Type} = { - 'tb-simple-card-basic-config': SimpleCardBasicConfigComponent, - 'tb-entities-table-basic-config': EntitiesTableBasicConfigComponent, - 'tb-timeseries-table-basic-config': TimeseriesTableBasicConfigComponent, - 'tb-flot-basic-config': FlotBasicConfigComponent, - 'tb-alarms-table-basic-config': AlarmsTableBasicConfigComponent, - 'tb-value-card-basic-config': ValueCardBasicConfigComponent, - 'tb-aggregated-value-card-basic-config': AggregatedValueCardBasicConfigComponent, - 'tb-alarm-count-basic-config': AlarmCountBasicConfigComponent, - 'tb-entity-count-basic-config': EntityCountBasicConfigComponent, - 'tb-battery-level-basic-config': BatteryLevelBasicConfigComponent, - 'tb-wind-speed-direction-basic-config': WindSpeedDirectionBasicConfigComponent, - 'tb-signal-strength-basic-config': SignalStrengthBasicConfigComponent, - 'tb-value-chart-card-basic-config': ValueChartCardBasicConfigComponent, - 'tb-progress-bar-basic-config': ProgressBarBasicConfigComponent, - 'tb-radial-gauge-basic-config': RadialGaugeBasicConfigComponent, - 'tb-thermometer-scale-gauge-basic-config': ThermometerScaleGaugeBasicConfigComponent, - 'tb-compass-gauge-basic-config': CompassGaugeBasicConfigComponent, - 'tb-liquid-level-card-basic-config': LiquidLevelCardBasicConfigComponent, - 'tb-doughnut-basic-config': DoughnutBasicConfigComponent, - 'tb-range-chart-basic-config': RangeChartBasicConfigComponent, - 'tb-bar-chart-with-labels-basic-config': BarChartWithLabelsBasicConfigComponent, - 'tb-single-switch-basic-config': SingleSwitchBasicConfigComponent, - 'tb-action-button-basic-config': ActionButtonBasicConfigComponent, - 'tb-segmented-button-basic-config': SegmentedButtonBasicConfigComponent, - 'tb-command-button-basic-config': CommandButtonBasicConfigComponent, - 'tb-power-button-basic-config': PowerButtonBasicConfigComponent, - 'tb-slider-basic-config': SliderBasicConfigComponent, - 'tb-toggle-button-basic-config': ToggleButtonBasicConfigComponent, - 'tb-time-series-chart-basic-config': TimeSeriesChartBasicConfigComponent, - 'tb-status-widget-basic-config': StatusWidgetBasicConfigComponent, - 'tb-pie-chart-basic-config': PieChartBasicConfigComponent, - 'tb-bar-chart-basic-config': BarChartBasicConfigComponent, - 'tb-polar-area-chart-basic-config': PolarAreaChartBasicConfigComponent, - 'tb-radar-chart-basic-config': RadarChartBasicConfigComponent, - 'tb-digital-simple-gauge-basic-config': DigitalSimpleGaugeBasicConfigComponent, - 'tb-mobile-app-qr-code-basic-config': MobileAppQrCodeBasicConfigComponent, - 'tb-label-card-basic-config': LabelCardBasicConfigComponent, - 'tb-label-value-card-basic-config': LabelValueCardBasicConfigComponent, - 'tb-unread-notification-basic-config': UnreadNotificationBasicConfigComponent, - 'tb-scada-symbol-basic-config': ScadaSymbolBasicConfigComponent -}; diff --git a/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts index ab63e0e55d..1f3e9bcbc7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts @@ -39,7 +39,6 @@ import { import { Subscription } from 'rxjs'; import { TranslateService } from '@ngx-translate/core'; import { DynamicFormData, IWidgetSettingsComponent, Widget, WidgetSettings } from '@shared/models/widget.models'; -import { widgetSettingsComponentsMap } from '@home/components/widget/lib/settings/widget-settings.module'; import { Dashboard } from '@shared/models/dashboard.models'; import { WidgetService } from '@core/http/widget.service'; import { IAliasController } from '@core/api/widget-api.models'; @@ -215,7 +214,7 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnDestroy, this.definedSettingsComponent = null; } if (this.settingsDirective && this.settingsDirective.length) { - const componentType = widgetSettingsComponentsMap[this.settingsDirective]; + const componentType = this.widgetService.getWidgetSettingsComponentTypeBySelector(this.settingsDirective); if (!componentType) { this.definedDirectiveError = this.translate.instant('widget-config.settings-component-not-found', {selector: this.settingsDirective}); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts index 4b5f3a1ad0..0aee57babb 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts @@ -14,14 +14,14 @@ /// limitations under the License. /// -import { NgModule, Type } from '@angular/core'; -import { - QrCodeWidgetSettingsComponent -} from '@home/components/widget/lib/settings/cards/qrcode-widget-settings.component'; +import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { SharedModule } from '@shared/shared.module'; +import { WidgetService } from '@core/http/widget.service'; import { SharedHomeComponentsModule } from '@home/components/shared-home-components.module'; -import { IWidgetSettingsComponent } from '@shared/models/widget.models'; +import { + QrCodeWidgetSettingsComponent +} from '@home/components/widget/lib/settings/cards/qrcode-widget-settings.component'; import { TimeseriesTableWidgetSettingsComponent } from '@home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component'; @@ -366,7 +366,7 @@ import { UnreadNotificationWidgetSettingsComponent } from '@home/components/widget/lib/settings/cards/unread-notification-widget-settings.component'; import { -ScadaSymbolWidgetSettingsComponent + ScadaSymbolWidgetSettingsComponent } from '@home/components/widget/lib/settings/scada/scada-symbol-widget-settings.component'; import { SegmentedButtonWidgetSettingsComponent @@ -643,105 +643,7 @@ import { ] }) export class WidgetSettingsModule { + constructor(private widgetService: WidgetService) { + this.widgetService.registerWidgetSettingsComponents(this.constructor) + } } - -export const widgetSettingsComponentsMap: {[key: string]: Type} = { - 'tb-qrcode-widget-settings': QrCodeWidgetSettingsComponent, - 'tb-mobile-app-qr-code-widget-settings': MobileAppQrCodeWidgetSettingsComponent, - 'tb-timeseries-table-widget-settings': TimeseriesTableWidgetSettingsComponent, - 'tb-timeseries-table-key-settings': TimeseriesTableKeySettingsComponent, - 'tb-timeseries-table-latest-key-settings': TimeseriesTableLatestKeySettingsComponent, - 'tb-markdown-widget-settings': MarkdownWidgetSettingsComponent, - 'tb-label-widget-settings': LabelWidgetSettingsComponent, - 'tb-simple-card-widget-settings': SimpleCardWidgetSettingsComponent, - 'tb-dashboard-state-widget-settings': DashboardStateWidgetSettingsComponent, - 'tb-entities-hierarchy-widget-settings': EntitiesHierarchyWidgetSettingsComponent, - 'tb-html-card-widget-settings': HtmlCardWidgetSettingsComponent, - 'tb-entities-table-widget-settings': EntitiesTableWidgetSettingsComponent, - 'tb-entities-table-key-settings': EntitiesTableKeySettingsComponent, - 'tb-alarms-table-widget-settings': AlarmsTableWidgetSettingsComponent, - 'tb-alarms-table-key-settings': AlarmsTableKeySettingsComponent, - 'tb-analogue-radial-gauge-widget-settings': AnalogueRadialGaugeWidgetSettingsComponent, - 'tb-analogue-linear-gauge-widget-settings': AnalogueLinearGaugeWidgetSettingsComponent, - 'tb-analogue-compass-widget-settings': AnalogueCompassWidgetSettingsComponent, - 'tb-digital-gauge-widget-settings': DigitalGaugeWidgetSettingsComponent, - 'tb-flot-line-widget-settings': FlotLineWidgetSettingsComponent, - 'tb-flot-bar-widget-settings': FlotBarWidgetSettingsComponent, - 'tb-flot-line-key-settings': FlotLineKeySettingsComponent, - 'tb-flot-bar-key-settings': FlotBarKeySettingsComponent, - 'tb-flot-latest-key-settings': FlotLatestKeySettingsComponent, - 'tb-flot-pie-widget-settings': FlotPieWidgetSettingsComponent, - 'tb-flot-pie-key-settings': FlotPieKeySettingsComponent, - 'tb-chart-widget-settings': ChartWidgetSettingsComponent, - 'tb-doughnut-chart-widget-settings': DoughnutChartWidgetSettingsComponent, - 'tb-round-switch-widget-settings': RoundSwitchWidgetSettingsComponent, - 'tb-switch-control-widget-settings': SwitchControlWidgetSettingsComponent, - 'tb-slide-toggle-widget-settings': SlideToggleWidgetSettingsComponent, - 'tb-persistent-table-widget-settings': PersistentTableWidgetSettingsComponent, - 'tb-update-device-attribute-widget-settings': UpdateDeviceAttributeWidgetSettingsComponent, - 'tb-send-rpc-widget-settings': SendRpcWidgetSettingsComponent, - 'tb-led-indicator-widget-settings': LedIndicatorWidgetSettingsComponent, - 'tb-knob-control-widget-settings': KnobControlWidgetSettingsComponent, - 'tb-rpc-terminal-widget-settings': RpcTerminalWidgetSettingsComponent, - 'tb-rpc-shell-widget-settings': RpcShellWidgetSettingsComponent, - 'tb-date-range-navigator-widget-settings': DateRangeNavigatorWidgetSettingsComponent, - 'tb-edge-quick-overview-widget-settings': EdgeQuickOverviewWidgetSettingsComponent, - 'tb-gateway-config-widget-settings': GatewayConfigWidgetSettingsComponent, - 'tb-gateway-config-single-device-widget-settings': GatewayConfigSingleDeviceWidgetSettingsComponent, - 'tb-gateway-events-widget-settings': GatewayEventsWidgetSettingsComponent, - 'tb-gpio-control-widget-settings': GpioControlWidgetSettingsComponent, - 'tb-gpio-panel-widget-settings': GpioPanelWidgetSettingsComponent, - 'tb-navigation-card-widget-settings': NavigationCardWidgetSettingsComponent, - 'tb-navigation-cards-widget-settings': NavigationCardsWidgetSettingsComponent, - 'tb-device-claiming-widget-settings': DeviceClaimingWidgetSettingsComponent, - 'tb-update-integer-attribute-widget-settings': UpdateIntegerAttributeWidgetSettingsComponent, - 'tb-update-double-attribute-widget-settings': UpdateDoubleAttributeWidgetSettingsComponent, - 'tb-update-string-attribute-widget-settings': UpdateStringAttributeWidgetSettingsComponent, - 'tb-update-boolean-attribute-widget-settings': UpdateBooleanAttributeWidgetSettingsComponent, - 'tb-update-image-attribute-widget-settings': UpdateImageAttributeWidgetSettingsComponent, - 'tb-update-date-attribute-widget-settings': UpdateDateAttributeWidgetSettingsComponent, - 'tb-update-location-attribute-widget-settings': UpdateLocationAttributeWidgetSettingsComponent, - 'tb-update-json-attribute-widget-settings': UpdateJsonAttributeWidgetSettingsComponent, - 'tb-photo-camera-input-widget-settings': PhotoCameraInputWidgetSettingsComponent, - 'tb-update-multiple-attributes-widget-settings': UpdateMultipleAttributesWidgetSettingsComponent, - 'tb-update-multiple-attributes-key-settings': UpdateMultipleAttributesKeySettingsComponent, - 'tb-map-widget-settings': MapWidgetSettingsComponent, - 'tb-route-map-widget-settings': RouteMapWidgetSettingsComponent, - 'tb-trip-animation-widget-settings': TripAnimationWidgetSettingsComponent, - 'tb-gateway-logs-settings': GatewayLogsSettingsComponent, - 'tb-gateway-service-rpc-settings':GatewayServiceRPCSettingsComponent, - 'tb-doc-links-widget-settings': DocLinksWidgetSettingsComponent, - 'tb-quick-links-widget-settings': QuickLinksWidgetSettingsComponent, - 'tb-value-card-widget-settings': ValueCardWidgetSettingsComponent, - 'tb-aggregated-value-card-key-settings': AggregatedValueCardKeySettingsComponent, - 'tb-aggregated-value-card-widget-settings': AggregatedValueCardWidgetSettingsComponent, - 'tb-alarm-count-widget-settings': AlarmCountWidgetSettingsComponent, - 'tb-entity-count-widget-settings': EntityCountWidgetSettingsComponent, - 'tb-battery-level-widget-settings': BatteryLevelWidgetSettingsComponent, - 'tb-wind-speed-direction-widget-settings': WindSpeedDirectionWidgetSettingsComponent, - 'tb-signal-strength-widget-settings': SignalStrengthWidgetSettingsComponent, - 'tb-value-chart-card-widget-settings': ValueChartCardWidgetSettingsComponent, - 'tb-progress-bar-widget-settings': ProgressBarWidgetSettingsComponent, - 'tb-liquid-level-card-widget-settings': LiquidLevelCardWidgetSettingsComponent, - 'tb-doughnut-widget-settings': DoughnutWidgetSettingsComponent, - 'tb-range-chart-widget-settings': RangeChartWidgetSettingsComponent, - 'tb-bar-chart-with-labels-widget-settings': BarChartWithLabelsWidgetSettingsComponent, - 'tb-single-switch-widget-settings': SingleSwitchWidgetSettingsComponent, - 'tb-action-button-widget-settings': ActionButtonWidgetSettingsComponent, - 'tb-segmented-button-widget-settings': SegmentedButtonWidgetSettingsComponent, - 'tb-command-button-widget-settings': CommandButtonWidgetSettingsComponent, - 'tb-power-button-widget-settings': PowerButtonWidgetSettingsComponent, - 'tb-slider-widget-settings': SliderWidgetSettingsComponent, - 'tb-toggle-button-widget-settings': ToggleButtonWidgetSettingsComponent, - 'tb-time-series-chart-key-settings': TimeSeriesChartKeySettingsComponent, - 'tb-time-series-chart-widget-settings': TimeSeriesChartWidgetSettingsComponent, - 'tb-status-widget-settings': StatusWidgetSettingsComponent, - 'tb-pie-chart-widget-settings': PieChartWidgetSettingsComponent, - 'tb-bar-chart-widget-settings': BarChartWidgetSettingsComponent, - 'tb-polar-area-chart-widget-settings': PolarAreaChartWidgetSettingsComponent, - 'tb-radar-chart-widget-settings': RadarChartWidgetSettingsComponent, - 'tb-label-card-widget-settings': LabelCardWidgetSettingsComponent, - 'tb-label-value-card-widget-settings': LabelValueCardWidgetSettingsComponent, - 'tb-unread-notification-widget-settings': UnreadNotificationWidgetSettingsComponent, - 'tb-scada-symbol-widget-settings': ScadaSymbolWidgetSettingsComponent -}; diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts index d9b18d896a..c044dca684 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts @@ -36,7 +36,8 @@ import { ResourcesService } from '@core/services/resources.service'; import { - IWidgetSettingsComponent, migrateWidgetTypeToDynamicForms, + IWidgetSettingsComponent, + migrateWidgetTypeToDynamicForms, Widget, widgetActionSources, WidgetControllerDescriptor, @@ -58,8 +59,6 @@ import tinycolor from 'tinycolor2'; import moment from 'moment'; import { IModulesMap } from '@modules/common/modules-map.models'; import { HOME_COMPONENTS_MODULE_TOKEN } from '@home/components/tokens'; -import { widgetSettingsComponentsMap } from '@home/components/widget/lib/settings/widget-settings.module'; -import { basicWidgetConfigComponentsMap } from '@home/components/widget/config/basic/basic-widget-config.module'; import { IBasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; import { compileTbFunction, TbFunction } from '@shared/models/js-function.models'; import { HttpClient } from '@angular/common/http'; @@ -437,17 +436,17 @@ export class WidgetComponentService { basicDirectives.push(widgetInfo.basicModeDirective); } - this.expandSettingComponentMap(widgetSettingsComponentsMap, directives, modulesWithComponents); - this.expandSettingComponentMap(basicWidgetConfigComponentsMap, basicDirectives, modulesWithComponents); + this.expandSettingComponentMap(this.widgetService.putWidgetSettingsComponentToMap, directives, modulesWithComponents); + this.expandSettingComponentMap(this.widgetService.putBasicWidgetSettingsComponentToMap, basicDirectives, modulesWithComponents); } - private expandSettingComponentMap(settingsComponentsMap: {[key: string]: Type}, + private expandSettingComponentMap(putComponentToMap: (selector: string, comp: Type) => void, directives: string[], modulesWithComponents: ModulesWithComponents): void { if (directives.length) { directives.forEach(selector => { const compType = componentTypeBySelector(modulesWithComponents, selector); if (compType) { - settingsComponentsMap[selector] = compType; + putComponentToMap(selector, compType); } }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts index 1721a7f068..595a8a2914 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts @@ -79,11 +79,11 @@ import { Filter, singleEntityFilterFromDeviceId } from '@shared/models/query/que import { FilterDialogComponent, FilterDialogData } from '@home/components/filter/filter-dialog.component'; import { ToggleHeaderOption } from '@shared/components/toggle-header.component'; import { coerceBoolean } from '@shared/decorators/coercion'; -import { basicWidgetConfigComponentsMap } from '@home/components/widget/config/basic/basic-widget-config.module'; import { TimewindowConfigData } from '@home/components/widget/config/timewindow-config-panel.component'; import { DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models'; import { defaultFormProperties, FormProperty } from '@shared/models/dynamic-form.models'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { WidgetService } from '@core/http/widget.service'; import Timeout = NodeJS.Timeout; @Component({ @@ -205,6 +205,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe public translate: TranslateService, private fb: UntypedFormBuilder, private cd: ChangeDetectorRef, + private widgetService: WidgetService, private destroyRef: DestroyRef) { super(store); } @@ -435,7 +436,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe } private setupBasicModeConfig(isAdd = false) { - const componentType = basicWidgetConfigComponentsMap[this.modelValue.basicModeDirective]; + const componentType = this.widgetService.getBasicWidgetSettingsComponentBySelector(this.modelValue.basicModeDirective); if (!componentType) { this.basicModeDirectiveError = this.translate.instant('widget-config.settings-component-not-found', {selector: this.modelValue.basicModeDirective}); From 152d901d3e78c3dbaa946fc8cdfa0ce2f0a704a2 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 4 Feb 2025 12:49:21 +0200 Subject: [PATCH 104/108] UI: Fixed lose context when use expandSettingComponentMap in custom widgets --- .../home/components/widget/widget-component.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts index c044dca684..8c94dfcdde 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts @@ -436,8 +436,8 @@ export class WidgetComponentService { basicDirectives.push(widgetInfo.basicModeDirective); } - this.expandSettingComponentMap(this.widgetService.putWidgetSettingsComponentToMap, directives, modulesWithComponents); - this.expandSettingComponentMap(this.widgetService.putBasicWidgetSettingsComponentToMap, basicDirectives, modulesWithComponents); + this.expandSettingComponentMap(this.widgetService.putWidgetSettingsComponentToMap.bind(this.widgetService), directives, modulesWithComponents); + this.expandSettingComponentMap(this.widgetService.putBasicWidgetSettingsComponentToMap.bind(this.widgetService), basicDirectives, modulesWithComponents); } private expandSettingComponentMap(putComponentToMap: (selector: string, comp: Type) => void, From 783579a093c7c79d4ecd5d9748c3981878838941 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 5 Feb 2025 11:56:51 +0200 Subject: [PATCH 105/108] UI: Fixed unclear cached result in dashboard autocomplete when changing user --- .../dashboard-autocomplete.component.html | 4 +++- .../components/dashboard-autocomplete.component.ts | 14 ++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ui-ngx/src/app/shared/components/dashboard-autocomplete.component.html b/ui-ngx/src/app/shared/components/dashboard-autocomplete.component.html index bfdf4dce2b..c8c6935619 100644 --- a/ui-ngx/src/app/shared/components/dashboard-autocomplete.component.html +++ b/ui-ngx/src/app/shared/components/dashboard-autocomplete.component.html @@ -31,7 +31,9 @@ [required]="required" [matAutocomplete]="dashboardAutocomplete" [class.!hidden]="useDashboardLink && disabled && selectDashboardFormGroup.get('dashboard').value"> - + {{ displayDashboardFn(selectDashboardFormGroup.get('dashboard').value) | customTranslate }}