From 12ca1739d4266c5110605808fd15acbd60b2c126 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 23 Oct 2024 15:30:07 +0300 Subject: [PATCH 01/40] 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.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 2c131f67677e7296eed26131a1c229a12e147930 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 12 Dec 2024 18:02:51 +0200 Subject: [PATCH 02/40] 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 03/40] 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.util.Objects; import java.util.UUID; +import static java.util.Objects.requireNonNull; + /** * Created by ashvayka on 13.01.18. */ @@ -81,6 +83,79 @@ public int getAndIncrementRuleNodeCounter() { 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 static TbMsg newMsg(String queueName, String type, EntityId originator, T */ @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 static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaD @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 static TbMsg newMsg(String queueName, TbMsgType type, EntityId originator } 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 static TbMsg newMsg(TbMsgType type, EntityId originator, TbMsgMetaData me } 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 static TbMsg newMsg(String queueName, String type, EntityId originator, T */ @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 static TbMsg newMsg(String type, EntityId originator, CustomerId customer */ @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 static TbMsg newMsg(String queueName, TbMsgType type, EntityId originator } 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 @@ private TbMsg(String queueName, UUID id, long ts, TbMsgType internalType, String 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 long getMetaDataTs() { } 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 04/40] 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 void updateSelf(RuleNode self) { 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 TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, TbMsg @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 @@ private TbMsg entityActionM 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 @@ private TbMsg entityActionM 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 void deleteRpc( 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 JsonNode testScript( } 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 void onSuccess(@Nullable DeferredResult result) { 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 void pushEntityActionToRuleEngine(EntityId entityId, HasName entity, Tena 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 @@ private DeviceCredentials getDeviceCredentials(Device device) { 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 @@ private void pushProvisionEventToRuleEngine(ProvisionRequest request, Device dev 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 @@ private void pushRuleEngineMessage(TenantId tenantId, Edge edge, long ts, TbMsgT 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 @@ protected TbMsgMetaData getEdgeActionTbMsgMetaData(Edge edge, CustomerId custome 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 @@ private ListenableFuture processDeviceRpcRequestFromEdge(TenantId tenantId 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 @@ private ListenableFuture processPostTelemetry(TenantId tenantId, CustomerI 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 @@ private ListenableFuture processPostAttributes(TenantId tenantId, Customer 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 @@ private ListenableFuture processAttributesUpdate(TenantId tenantId, @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 @@ private void onEdgeEvent(TenantId tenantId, EntityId entityId, Object entity, Co 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 @@ private void sendRpcRequestToRuleEngine(ToDeviceRpcRequest msg, SecurityUser cur 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 void save(TenantId tenantId, RpcId rpcId, RpcStatus newStatus, JsonNode r } 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 @@ private void pushRuleEngineMessage(DeviceStateData stateData, TbMsgType msgType) if (!persistToTelemetry) { md.putValue(SCOPE, SERVER_SCOPE); } - TbMsg tbMsg = TbMsg.newMsg(msgType, stateData.getDeviceId(), stateData.getCustomerId(), md, TbMsgDataType.JSON, data); + TbMsg tbMsg = TbMsg.builder() + .type(msgType) + .originator(stateData.getDeviceId()) + .customerId(stateData.getCustomerId()) + .metaData(md.copy()) + .dataType(TbMsgDataType.JSON) + .data(data) + .build(); clusterService.pushMsgToRuleEngine(stateData.getTenantId(), stateData.getDeviceId(), tbMsg, null); } catch (Exception e) { log.warn("[{}] Failed to push inactivity alarm: {}", stateData.getDeviceId(), state, e); diff --git a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java index 51d37e291a..bf9be485f4 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java @@ -362,7 +362,14 @@ private TransportApiResponseMsg handle(GetOrCreateDeviceFromGatewayRequestMsg re DeviceId deviceId = device.getId(); JsonNode entityNode = JacksonUtil.valueToTree(device); - TbMsg tbMsg = TbMsg.newMsg(TbMsgType.ENTITY_CREATED, deviceId, customerId, metaData, TbMsgDataType.JSON, JacksonUtil.toString(entityNode)); + TbMsg tbMsg = TbMsg.builder() + .type(TbMsgType.ENTITY_CREATED) + .originator(deviceId) + .customerId(customerId) + .metaData(metaData.copy()) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(entityNode)) + .build(); tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, null); } else { JsonNode deviceAdditionalInfo = device.getAdditionalInfo(); diff --git a/application/src/test/java/org/thingsboard/server/actors/rule/DefaultTbContextTest.java b/application/src/test/java/org/thingsboard/server/actors/rule/DefaultTbContextTest.java index 21a4a45c6f..54a4ca8e78 100644 --- a/application/src/test/java/org/thingsboard/server/actors/rule/DefaultTbContextTest.java +++ b/application/src/test/java/org/thingsboard/server/actors/rule/DefaultTbContextTest.java @@ -928,15 +928,32 @@ private static Stream givenDebugFailuresAndDebugAllAndConnectionAndPe } private TbMsg getTbMsgWithCallback(TbMsgCallback callback) { - return TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TENANT_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING, callback); + return TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(TENANT_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_STRING) + .callback(callback) + .build(); } private TbMsg getTbMsgWithQueueName() { - return TbMsg.newMsg(DataConstants.MAIN_QUEUE_NAME, TbMsgType.POST_TELEMETRY_REQUEST, TENANT_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + return TbMsg.builder() + .queueName(DataConstants.MAIN_QUEUE_NAME) + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(TENANT_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); } private TbMsg getTbMsg() { - return TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TENANT_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + return TbMsg.builder() + .type(TbMsgType.POST_TELEMETRY_REQUEST) + .originator(TENANT_ID) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(TbMsg.EMPTY_STRING) + .build(); } private static long getUntilTime() { diff --git a/application/src/test/java/org/thingsboard/server/controller/RuleEngineControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/RuleEngineControllerTest.java index 9c9322dc38..290336db4d 100644 --- a/application/src/test/java/org/thingsboard/server/controller/RuleEngineControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/RuleEngineControllerTest.java @@ -62,7 +62,12 @@ public class RuleEngineControllerTest extends AbstractControllerTest { @Test public void testHandleRuleEngineRequestWithMsgOriginatorUser() throws Exception { loginSysAdmin(); - TbMsg responseMsg = TbMsg.newMsg(TbMsgType.REST_API_REQUEST, currentUserId, TbMsgMetaData.EMPTY, RESPONSE_BODY); + TbMsg responseMsg = TbMsg.builder() + .type(TbMsgType.REST_API_REQUEST) + .originator(currentUserId) + .metaData(TbMsgMetaData.EMPTY.copy()) + .data(RESPONSE_BODY) + .build(); mockRestApiCallToRuleEngine(responseMsg); JsonNode apiResponse = doPostAsyncWithTypedResponse("/api/rule-engine/", REQUEST_BODY, new TypeReference<>() { @@ -86,7 +91,12 @@ public void testHandleRuleEngineRequestWithMsgOriginatorDevice() throws Exceptio 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 void testHandleRuleEngineRequestWithMsgOriginatorDeviceAndSpecifiedTimeou 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 void testHandleRuleEngineRequestWithMsgOriginatorDeviceAndSpecifiedQueue( 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 void testHandleRuleEngineRequestWithAuthorityCustomerUser() throws Except 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 void whenTenantIsDeleted_thenDeleteQueues() throws Exception { } 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 void testRuleChainWithTwoRules() throws Exception { 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 void testTwoRuleChainsWithTwoRules() throws Exception { 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 void testRuleChainWithOneRule() throws Exception { 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 void testPushMsgToRuleEngineWithTenantIdIsNullUuidAndEntityIsTenantUseQue 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 void testPushMsgToRuleEngineWithTenantIdIsNullUuidAndEntityIsTenantUseQue 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 void testPushMsgToRuleEngineWithTenantIdIsNotNullUuidUseQueueFromMsgIsTru 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 void testPushMsgToRuleEngineUseQueueFromMsgIsFalse() { 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 void testGetRuleEngineProfileForUpdatedAndDeletedDevice() { 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 void testGetRuleEngineProfileForUpdatedAndDeletedAsset() { 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 Set getPartitions() { } 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 @@ public void givenTbMsg_whenSendRestApiCallReply_thenPushNotificationToCore() { 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 @@ void givenRequest_whenProcessRestApiCallToRuleEngine_thenPushMsgToRuleEngineAndC 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 @@ void givenResponse_whenOnQueue_thenAcceptTbMsgResponse() { 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 @@ Asset saveAsset(String name) throws Exception { 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.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 @@ @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 static TbMsg newMsg(TbMsg tbMsg, String queueName, RuleChainId ruleChainI 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 @@ private TbMsg(String queueName, UUID id, long ts, TbMsgType internalType, String } 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 boolean isTypeOneOf(TbMsgType... types) { 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.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 @@ private void sendToRuleEngine(TenantId tenantId, DeviceId deviceId, CustomerId c 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 void testSimpleReplacement() { 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 void testNoReplacement() { 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 void testSameKeysReplacement() { 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 void testComplexObjectReplacement() { 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 void testArrayReplacementDoesNotWork() { 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 void onMsg(TbContext ctx, TbMsg msg) { 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 @@ private void processDeduplication(TbContext ctx, EntityId deduplicationId) { 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 @@ private void processDeduplication(TbContext ctx, EntityId deduplicationId) { } } 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 void onMsg(TbContext ctx, TbMsg msg) { 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.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 @@ protected DeviceState getOrCreateDeviceState(TbContext ctx, DeviceId deviceId, R } 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 @@ protected void updateProfile(TbContext ctx, DeviceProfileId deviceProfileId) thr } 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 @@ private void onDeviceUpdate(DeviceId deviceId, DeviceProfile deviceProfile) { 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 @@ private Customer createCustomer(String customerTitle) { } 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 @@ void before() { 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 @@ void alarmCanBeCleared() { 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 @@ void setUp() throws TbNodeException { 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 void givenExistingClientAttributes_whenOnMsg_thenCopyAttributesToView() { 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 @@ ATTRIBUTES_DELETED, DEVICE_ID, new TbMsgMetaData(Map.of(DataConstants.SCOPE, Att 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 @@ TbMsgType.ATTRIBUTES_DELETED, DEVICE_ID, new TbMsgMetaData(Map.of(DataConstants. 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 void givenAttributesValidityPeriodOutOfStartDateAndEndDate_whenOnMsg_then ); 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 @@ ATTRIBUTES_DELETED, DEVICE_ID, new TbMsgMetaData(Map.of(DataConstants.SCOPE, Att @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 @@ void whenAlarmDataIsTakenFromDefaultNodeConfigAndAlarmDoesNotExist_thenNewAlarmI 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 @@ void whenAlarmDataIsTakenFromNodeConfigAndClearedAlarmExists_thenNewAlarmIsCreat 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 @@ void whenAlarmDataIsTakenFromNodeConfigAndActiveAlarmExists_thenExistingAlarmIsU 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 @@ void whenAlarmDataIsTakenFromMsgAndClearedAlarmExists_thenNewAlarmIsCreated() th 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 @@ void whenAlarmDataIsTakenFromMsgAndActiveAlarmExists_thenExistingAlarmIsUpdated( .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 @@ void whenOnlySeverityWasUpdated_thenShouldTakeAlarmUpdatedPath() throws Exceptio .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 @@ void whenAlarmDetailsScriptThrowsException_thenShouldTellFailureAndNoOtherAction // 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 @@ void givenSupportedEntityType_whenOnMsg_thenVerifyRelationAndEntityCreatedAndOut 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 @@ private Map mockEntityServiceCallsEntityNotFound() { } 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 @@ private Map> verifyEntityServiceCalls() { } 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 void setup() { 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 EntityType getEntityType() { 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 void givenMetadataDoesNotContainTs_whenOnMsg_thenMsgTsIsUsedAsEventTs() { 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 @@ void givenMsg_whenToLog_thenReturnString() { 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 @@ void givenMsg_whenToLog_thenReturnString() { 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 @@ void givenEmptyDataMsg_whenToLog_thenReturnString() { 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 void givenIncomingMsgs_whenOnMsg_thenSendsMsgWithMsgCount() throws TbNode 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 void givenIncomingMsgs_whenOnMsg_thenSendsMsgWithMsgCount() throws TbNode 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 void givenInvalidMessageStructure_whenOnMsg_thenThrowsException() throws 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 void givenDataKeyIsMissingInMsg_whenOnMsg_thenThrowsException() throws Tb "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 void givenUnsupportedData_whenOnMsg_thenThrowsException() throws TbNodeEx "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 void givenTtl_whenOnMsg_thenVerifyStatement(int ttlFromConfig, 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 void givenValidMsgStructure_whenOnMsg_thenVerifyMatchOfValuesInsertionOrd } } """; - 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 @@ private Customer createCustomer(String customerTitle) { } 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 void givenRequest_whenOnMsg_thenTellSuccess(String data, TbMsgMetaData me 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 void givenExceptionWasThrownInsideFunctionAndTellFailureIfFuncThrowsExcIs 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 void givenExceptionWasThrownInsideFunctionAndTellFailureIfFuncThrowsExcIs 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 void givenExceptionWasThrownInsideFunctionAndTellFailureIfFuncThrowsExcIs 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 void givenPayloadFromResultIsNull_whenOnMsg_thenTellFailure() { @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 @@ void givenForceAckIsTrueAndTopicNamePattern_whenOnMsg_thenEnqueueForTellNext(Str 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 @@ void givenForceAckIsFalseAndErrorOccursDuringProcessingRequest_whenOnMsg_thenTel 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 @@ void givenQueueUrlPatternsAndQueueTypeIsFifo_whenOnMsg_thenVerifyRequest(String 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 @@ void givenMsgAttributesPatternsAndQueueTypeIsStandard_whenOnMsg_thenVerifyReques 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 @@ void givenForceAckIsTrueAndMsgResultContainsBodyAndAttributesAndNumber_whenOnMsg 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 @@ void givenForceAckIsFalseAndErrorOccursDuringProcessingRequest_whenOnMsg_thenTel 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 void givenMsgCountAndDelay_whenInit_thenVerifyInvocationOfOnMsgMethod() t 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 void givenMsgCountAndDelay_whenInit_thenVerifyInvocationOfOnMsgMethod() t }).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 void ackMsgInCaseNoEdgeRelated() { 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 void testAttributeUpdateMsg_userEntity() { 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 @@ private void testEvent(TbMsgType event, TbMsgMetaData metaData, EdgeEventActionT 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 @@ void givenMsg_whenOnMsg_then_Success() throws TbNodeException { } 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 @@ void givenUnparseableAlarm_whenOnMsg_then_Failure() { } 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 @@ 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 @@ private TbMsg getTbMsg(boolean emptyData) { 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 @@ void givenMsg_whenOnMsg_then_Success() throws TbNodeException { } 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 void falseEvaluationDoNotSendMsg() throws TbNodeException { 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 void exceptionInJsThrowsException() throws TbNodeException { 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 void multipleRoutesAreAllowed() throws TbNodeException { 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 @@ void givenAttributesUpdated_whenOnMsg_then_False() { } 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 @@ void givenAllTypes_whenOnMsg_then_allTypesSupported() throws TbNodeException { } 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 @@ void givenAsset_whenOnMsg_then_False() { } 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 @@ void givenAllTypes_whenOnMsg_then_allTypesSupported() throws TbNodeException { } 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 void givenDefaultConfig_whenInit_thenOk() { 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 void givenQueueName_whenOnMsg_thenTransfersMsgToDefinedQueue(String queue 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 void givenErrorDuringTransfer_whenOnMsg_thenTellFailure() throws TbNodeEx 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 @@ protected TbNode getTestNode() { } 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 void givenRuleNodeName_whenOnMsg_thenForwardMsgToTheCallerRuleChainWithRe 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 @@ public void givenForceAckIsTrueAndMessageAttributesPatterns_whenOnMsg_thenEnqueu 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 @@ public void givenForceAckIsFalse_whenOnMsg_thenTellSuccess() throws IOException, 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 @@ public void givenForceAckIsFalseAndErrorOccursOnTheGCP_whenOnMsg_thenTellFailure 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 @@ public void givenForceAckIsTrueAndErrorOccursOnTheGCP_whenOnMsg_thenEnqueueForTe 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 @@ private TbMsg getInsideRectangleTbMsg(EntityId entityId) { 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 @@ private TbMsgMetaData getMetadataForNewVersionCirclePerimeter() { 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 void givenInitErrorIsNotNull_whenOnMsg_thenTellFailure() { 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 void givenForceAckAndExceptionWasThrown_whenOnMsg_thenTellFailure(boolean // 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 void givenForceAckIsTrueTopicAndKeyPatternsAndAddMetadataKeyValuesAsKafka // 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 void givenForceAckIsFalseAndKeyIsNullOrEmptyAndErrorOccursDuringPublishin // 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 void givenForceAckIsTrueAndAddKafkaHeadersIsTrueAndToBytesCharsetIsNullAn // 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 void givenForceAckIsFalseAndAddMetadataKeyValuesAsKafkaHeadersIsTrueAndTo 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 void givenMailBodyTypeTestConfig_whenOnMsg_thenVerify(MailBodyTypeTestCon } 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 void testExp4j() { 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 void testExp4j() { 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 void testSimpleTwoArgumentFunction(TbRuleNodeMathFunctionType function, d 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 void testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType function, d 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 void test_2_plus_2_body() { 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 void test_2_plus_2_meta() { 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 void test_2_plus_2_attr_and_ts() { 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 void test_sqrt_5_body() { 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 void test_sqrt_5_meta() { 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 void test_sqrt_5_to_attribute_and_metadata() { 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 void test_sqrt_5_to_timeseries_and_data() { 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 void test_sqrt_5_to_timeseries_and_metadata_and_data() { 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 void test_sqrt_5_default_value() { 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 void test_sqrt_5_default_value_failure() { 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 void testConvertMsgBodyIfRequiredFailure() { 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 void testExp4j_concurrent() { 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 void testExp4j_concurrentBySingleOriginator_processMsgAsyncException() { 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 void testExp4j_concurrentBySingleOriginator_SingleMsg_manyNodesWithDiffer }) .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 void givenInvalidMsgType_whenOnMsg_thenShouldTellNextOther() throws TbNod // 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 void givenInvalidMsgType_whenOnMsg_thenShouldTellNextOther() throws TbNod 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 void givenInvalidMsgDataType_whenOnMsg_thenShouldTellNextOther() throws T 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 void givenDoubleValue_whenOnMsgAndCachingOff_thenShouldTellSuccess() thro 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 void givenLongStringValue_whenOnMsgAndCachingOff_thenShouldTellSuccess() 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 void givenValidStringValue_whenOnMsgAndCachingOff_thenShouldTellSuccess() 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 void givenTwoMessagesAndPeriodOnAndCachingOn_whenOnMsg_thenVerify() throw 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 void givenTwoMessagesAndPeriodOnAndCachingOn_whenOnMsg_thenVerify() throw 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 void givenLastValueIsNull_whenOnMsgAndCachingOff_thenDeltaShouldBeZero() 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 void givenNegativeDeltaAndTellFailureIfNegativeDeltaTrue_whenOnMsg_thenSh 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 void givenNegativeDeltaAndTellFailureIfNegativeDeltaFalse_whenOnMsg_thenS 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 void givenInvalidStringValue_whenOnMsg_thenException() throws TbNodeExcep 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 void givenBooleanValue_whenOnMsg_thenException() throws TbNodeException { 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 void givenJsonValue_whenOnMsg_thenException() throws TbNodeException { 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 void givenConcurrentAccess_whenOnMsg_thenGetFromDBInvokedOnce() throws Tb 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 void givenCalculateDeltaConfig_whenOnMsg_thenVerify(CalculateDeltaTestCon 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 @@ private TbMsg getTbMsg(EntityId entityId) { 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 @@ 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 void givenFetchLatestTimeseriesToData_whenOnMsg_thenShouldTellFailure() t 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 @@ private TbMsg getTbMsg(EntityId entityId) { 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 void givenEmptyAttributesMapping_whenInit_thenException() { 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 void givenDidNotFindEntity_whenOnMsg_thenShouldTellFailure() { // 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 @@ private void prepareMsgAndConfig(TbMsgSource fetchTo, DataToFetch dataToFetch, E 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 void givenCustomConfig_whenInit_thenOK() throws TbNodeException { 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 @@ private void prepareMsgAndConfig(TbMsgSource fetchTo, List 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 void givenCustomConfig_whenInit_thenOK() throws TbNodeException { 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 void givenValidMsgAndFetchToData_whenOnMsg_thenShouldTellSuccessAndFetchT 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 void givenDeviceWithEmptyLabel_whenOnMsg_thenShouldTellSuccessAndFetchToD 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 void givenValidMsgAndFetchToMetaData_whenOnMsg_thenShouldTellSuccessAndFe "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 void givenNullEntityFieldsAndIgnoreNullStringsFalse_whenOnMsg_thenShouldT "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 void givenUnsupportedEntityType_whenOnMsg_thenShouldTellFailureWithSameMs "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 void givenEmptyAttributesMapping_whenInit_thenException() { 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 @@ private void prepareMsgAndConfig(TbMsgSource fetchTo, DataToFetch dataToFetch, E 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 void givenIntervalStartIsGreaterThanIntervalEnd_whenOnMsg_thenThrowsExcep // 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 void givenUseMetadataIntervalPatternsIsTrue_whenOnMsg_thenVerifyStartAndE 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 void givenUseMetadataIntervalPatternsIsFalse_whenOnMsg_thenVerifyStartAnd 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 void givenTsKeyNamesPatterns_whenOnMsg_thenVerifyTsKeyNamesInQuery() thro // 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 void givenAggregation_whenOnMsg_thenVerifyAggregationStepInQuery(Aggregat 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 void givenFetchModeAndLimit_whenOnMsg_thenVerifyLimitInQuery(FetchMode fe 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 void givenFetchModeAndOrder_whenOnMsg_thenVerifyOrderInQuery(FetchMode fe 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 void givenInvalidIntervalPatterns_whenOnMsg_thenThrowsException(String st 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 void givenFetchModeAll_whenOnMsg_thenTellSuccessAndVerifyMsg() throws TbN 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 void givenFetchMode_whenOnMsg_thenTellSuccessAndVerifyMsg(String fetchMod 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 void givenEmptyAttributesMapping_whenInit_thenException() { 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 @@ private void prepareMsgAndConfig(TbMsgSource fetchTo, DataToFetch dataToFetch, E 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 void givenCustomConfig_whenInit_thenOK() throws TbNodeException { 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 @@ private void prepareMsgAndConfig(TbMsgSource fetchTo, List { 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 void whenAttributeIsDeleted_thenUnneededAlarmRulesAreNotReevaluated() thr 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 void whenAttributeIsDeleted_thenUnneededAlarmRulesAreNotReevaluated() thr 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 void whenDeletingClearedAlarm_thenNoError() throws Exception { 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 void testRandomMessageType() throws Exception { 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 void testEmptyProfile() throws Exception { 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 void testAlarmCreate() throws Exception { 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 void testAlarmSeverityUpdate() throws Exception { 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 void testAlarmSeverityUpdate() throws Exception { 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 void testConstantKeyFilterSimple() throws Exception { 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 void testConstantKeyFilterInherited() throws Exception { 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 void testCurrentDeviceAttributeForDynamicValue() throws Exception { 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 void testCurrentDeviceAttributeForDynamicDurationValue() throws Exception 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 void testCurrentDeviceAttributeForDynamicDurationValue() throws Exception 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 void testInheritTenantAttributeForDuration() throws Exception { 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 void testInheritTenantAttributeForDuration() throws Exception { 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 void testCurrentDeviceAttributeForDynamicRepeatingValue() throws Exceptio 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 void testCurrentDeviceAttributeForDynamicRepeatingValue() throws Exceptio 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 void testInheritTenantAttributeForRepeating() throws Exception { 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 void testInheritTenantAttributeForRepeating() throws Exception { 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 void testCurrentDeviceAttributeForUseDefaultDurationWhenDynamicDurationVa 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 void testCurrentDeviceAttributeForUseDefaultDurationWhenDynamicDurationVa 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 void testCurrentDeviceAttributeForUseDefaultRepeatingWhenDynamicDurationV 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 void testActiveAlarmScheduleFromDynamicValuesWhenDefaultScheduleIsInactiv 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 void testInactiveAlarmScheduleFromDynamicValuesWhenDefaultScheduleIsActiv 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 void testCurrentCustomersAttributeForDynamicValue() throws Exception { 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 void testCurrentTenantAttributeForDynamicValue() throws Exception { 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 void testTenantInheritModeForDynamicValues() throws Exception { 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 void testCustomerInheritModeForDynamicValues() throws Exception { 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 void givenForceAckIsTrueAndExchangeNameAndRoutingKeyPatternsAndBasicPrope 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 void givenForceAckIsFalseAndExchangeNameAndRoutingKeyPatternsAndBasicProp 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 void givenForceAckAndErrorOccursDuringPublishing_whenOnMsg_thenVerifyTell 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 void testProcessMessageWithJsonInUrlVariable() throws Exception { 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 void run() { 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 void run() { 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 void givenValidRestApiRequest_whenOnMsg_thenTellSuccess(String requestIdA 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 @@ private static Stream givenValidRestApiRequest_whenOnMsg_thenTellSucc @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 void setUp() throws TbNodeException { 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 void sendReplyToEdgeQueue() { 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 void sendReplyToEdgeQueue() { @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 void testOriginatorEntityTypes(EntityType entityType) { @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 void givenOneway_whenOnMsg_thenVerifyRequest(String mdKeyValue, boolean e 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 void givenMsgBody_whenOnMsg_thenVerifyRequest() { 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 void givenRequestIdIsNotSet_whenOnMsg_thenVerifyRequest() { 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 void givenRequestId_whenOnMsg_thenVerifyRequest() { "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 void givenRequestUUID_whenOnMsg_thenVerifyRequest() { 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 void givenInvalidRequestUUID_whenOnMsg_thenVerifyRequest(String requestUU 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 void givenOriginServiceId_whenOnMsg_thenVerifyRequest() { 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 void givenInvalidOriginServiceId_whenOnMsg_thenVerifyRequest(String origi 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 void givenExpirationTime_whenOnMsg_thenVerifyRequest() { 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 void givenInvalidExpirationTime_whenOnMsg_thenVerifyRequest(String expira 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 void givenRetries_whenOnMsg_thenVerifyRequest() { 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 void givenInvalidRetriesValue_whenOnMsg_thenVerifyRequest(String retries) 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 void givenTbMsgType_whenOnMsg_thenVerifyRequest(TbMsgType msgType) { 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 void givenPersistent_whenOnMsg_thenVerifyRequest(String isPersisted, bool 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 @@ private ArgumentCaptor captureRequest() { @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 void givenRpcResponseWithoutError_whenOnMsg_thenSendsRpcRequest() { 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 void givenRpcResponseWithoutError_whenOnMsg_thenSendsRpcRequest() { @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 void givenRpcResponseWithError_whenOnMsg_thenTellFailure() { 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 void givenRpcResponseWithError_whenOnMsg_thenTellFailure() { 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 void givenOriginatorIsNotDevice_whenOnMsg_thenThrowsException(EntityType @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 @@ void givenNotifyDeviceMdValue_whenSaveAndNotify_thenVerifyExpectedArgumentForNot 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 @@ void onMsg_thenVerifyOutput(boolean sendAttributesDeletedNotification, boolean n } 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 void verifyDefaultConfig() { @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 void givenTtlFromConfigIsZeroAndUseServiceTsIsTrue_whenOnMsg_thenSaveTime "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 void givenSkipLatestPersistenceIsTrueAndTtlFromConfig_whenOnMsg_thenSaveT """; 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 void givenTtlFromConfigAndTtlFromMd_whenOnMsg_thenVerifyTtl(String ttlFro """; 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 void givenOriginatorSourceIsCustomer_whenOnMsg_thenTellSuccess() throws T 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 void givenOriginatorSourceIsCustomer_whenOnMsg_thenTellSuccess() throws T 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 void givenOriginatorSourceIsTenant_whenOnMsg_thenTellSuccess() throws TbN 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 void givenOriginatorSourceIsAlarmOriginator_whenOnMsg_thenTellSuccess() t 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 void givenOriginatorSourceIsEntity_whenOnMsg_thenTellSuccess(String entit 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 void givenOriginatorSourceIsEntityAndEntityCouldNotFound_whenOnMsg_thenTe 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 @@ private TbMsg getTbMsg(EntityId entityId, String data) { "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 @@ private TbMsg getTbMsg(EntityId entityId, String data) { "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 @@ private TbMsg getTbMsg(EntityId entityId, String data) { 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 void init() throws TbNodeException { 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 @@ private TbMsg createMsg(DeviceId deviceId, long ts) { 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 @@ private TbMsg getTbMsg(EntityId entityId, String data) { "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 @@ private TbMsg getTbMsg(EntityId entityId, String data) { "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 void metadataCanBeUpdated() throws TbNodeException { 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 void exceptionHandledCorrectly() throws TbNodeException { 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 05/40] 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 void updateSelf(RuleNode self) { @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 TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, TbMsg @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 @@ private TbMsg entityActionM defaultQueueName = profile.getDefaultQueueName(); defaultRuleChainId = profile.getDefaultRuleChainId(); } - return TbMsg.builder() + return TbMsg.newMsg() .queueName(defaultQueueName) .type(action) .originator(id) @@ -536,7 +536,7 @@ private TbMsg entityActionM 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 void deleteRpc( 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 JsonNode testScript( } 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 void onSuccess(@Nullable DeferredResult result) { 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 void pushEntityActionToRuleEngine(EntityId entityId, HasName entity, Tena 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 @@ private DeviceCredentials getDeviceCredentials(Device device) { 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 @@ private void pushProvisionEventToRuleEngine(ProvisionRequest request, Device dev 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 @@ private void pushRuleEngineMessage(TenantId tenantId, Edge edge, long ts, TbMsgT 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 @@ protected TbMsgMetaData getEdgeActionTbMsgMetaData(Edge edge, CustomerId custome 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 @@ private ListenableFuture processDeviceRpcRequestFromEdge(TenantId tenantId 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 @@ private ListenableFuture processPostTelemetry(TenantId tenantId, CustomerI 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 @@ private ListenableFuture processPostAttributes(TenantId tenantId, Customer 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 @@ private ListenableFuture processAttributesUpdate(TenantId tenantId, @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 @@ private void onEdgeEvent(TenantId tenantId, EntityId entityId, Object entity, Co 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 @@ private void sendRpcRequestToRuleEngine(ToDeviceRpcRequest msg, SecurityUser cur 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 void save(TenantId tenantId, RpcId rpcId, RpcStatus newStatus, JsonNode r } 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 @@ private void pushRuleEngineMessage(DeviceStateData stateData, TbMsgType msgType) if (!persistToTelemetry) { md.putValue(SCOPE, SERVER_SCOPE); } - TbMsg tbMsg = TbMsg.builder() + TbMsg tbMsg = TbMsg.newMsg() .type(msgType) .originator(stateData.getDeviceId()) .customerId(stateData.getCustomerId()) diff --git a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java index bf9be485f4..c27a38df15 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java @@ -362,7 +362,7 @@ private TransportApiResponseMsg handle(GetOrCreateDeviceFromGatewayRequestMsg re DeviceId deviceId = device.getId(); JsonNode entityNode = JacksonUtil.valueToTree(device); - TbMsg tbMsg = TbMsg.builder() + TbMsg tbMsg = TbMsg.newMsg() .type(TbMsgType.ENTITY_CREATED) .originator(deviceId) .customerId(customerId) diff --git a/application/src/test/java/org/thingsboard/server/actors/rule/DefaultTbContextTest.java b/application/src/test/java/org/thingsboard/server/actors/rule/DefaultTbContextTest.java index 54a4ca8e78..f85f990f9e 100644 --- a/application/src/test/java/org/thingsboard/server/actors/rule/DefaultTbContextTest.java +++ b/application/src/test/java/org/thingsboard/server/actors/rule/DefaultTbContextTest.java @@ -928,7 +928,7 @@ private static Stream givenDebugFailuresAndDebugAllAndConnectionAndPe } private TbMsg getTbMsgWithCallback(TbMsgCallback callback) { - return TbMsg.builder() + return TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(TENANT_ID) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -938,7 +938,7 @@ private TbMsg getTbMsgWithCallback(TbMsgCallback callback) { } private TbMsg getTbMsgWithQueueName() { - return TbMsg.builder() + return TbMsg.newMsg() .queueName(DataConstants.MAIN_QUEUE_NAME) .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(TENANT_ID) @@ -948,7 +948,7 @@ private TbMsg getTbMsgWithQueueName() { } private TbMsg getTbMsg() { - return TbMsg.builder() + return TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(TENANT_ID) .metaData(TbMsgMetaData.EMPTY.copy()) diff --git a/application/src/test/java/org/thingsboard/server/controller/RuleEngineControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/RuleEngineControllerTest.java index 290336db4d..c9e08c02c2 100644 --- a/application/src/test/java/org/thingsboard/server/controller/RuleEngineControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/RuleEngineControllerTest.java @@ -62,7 +62,7 @@ public class RuleEngineControllerTest extends AbstractControllerTest { @Test public void testHandleRuleEngineRequestWithMsgOriginatorUser() throws Exception { loginSysAdmin(); - TbMsg responseMsg = TbMsg.builder() + TbMsg responseMsg = TbMsg.newMsg() .type(TbMsgType.REST_API_REQUEST) .originator(currentUserId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -91,7 +91,7 @@ public void testHandleRuleEngineRequestWithMsgOriginatorDevice() throws Exceptio loginTenantAdmin(); Device device = createDevice("Test", "123"); DeviceId deviceId = device.getId(); - TbMsg responseMsg = TbMsg.builder() + TbMsg responseMsg = TbMsg.newMsg() .type(TbMsgType.REST_API_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -120,7 +120,7 @@ public void testHandleRuleEngineRequestWithMsgOriginatorDeviceAndSpecifiedTimeou loginTenantAdmin(); Device device = createDevice("Test", "123"); DeviceId deviceId = device.getId(); - TbMsg responseMsg = TbMsg.builder() + TbMsg responseMsg = TbMsg.newMsg() .type(TbMsgType.REST_API_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -171,7 +171,7 @@ public void testHandleRuleEngineRequestWithMsgOriginatorDeviceAndSpecifiedQueue( loginTenantAdmin(); Device device = createDevice("Test", "123"); DeviceId deviceId = device.getId(); - TbMsg responseMsg = TbMsg.builder() + TbMsg responseMsg = TbMsg.newMsg() .queueName(DataConstants.HP_QUEUE_NAME) .type(TbMsgType.REST_API_REQUEST) .originator(deviceId) @@ -216,7 +216,7 @@ public void testHandleRuleEngineRequestWithAuthorityCustomerUser() throws Except assignDeviceToCustomer(deviceId, customerId); loginCustomerUser(); - TbMsg responseMsg = TbMsg.builder() + TbMsg responseMsg = TbMsg.newMsg() .type(TbMsgType.REST_API_REQUEST) .originator(deviceId) .customerId(customerId) 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 f2f843e448..b09d2752cf 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,7 @@ public void whenTenantIsDeleted_thenDeleteQueues() throws Exception { } private TbMsg publishTbMsg(TenantId tenantId, TopicPartitionInfo tpi) { - TbMsg tbMsg = TbMsg.builder() + TbMsg tbMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(tenantId) .metaData(TbMsgMetaData.EMPTY.copy()) 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 c47c847241..9926fb2c01 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,7 @@ public void testRuleChainWithTwoRules() throws Exception { TbMsgCallback tbMsgCallback = Mockito.mock(TbMsgCallback.class); Mockito.when(tbMsgCallback.isMsgValid()).thenReturn(true); - TbMsg tbMsg = TbMsg.builder() + TbMsg tbMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(device.getId()) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -315,7 +315,7 @@ public void testTwoRuleChainsWithTwoRules() throws Exception { TbMsgCallback tbMsgCallback = Mockito.mock(TbMsgCallback.class); Mockito.when(tbMsgCallback.isMsgValid()).thenReturn(true); - TbMsg tbMsg = TbMsg.builder() + TbMsg tbMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(device.getId()) .metaData(TbMsgMetaData.EMPTY.copy()) 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 780bd3c8cb..7618a6a373 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,7 @@ public void testRuleChainWithOneRule() throws Exception { log.warn("attr updated"); TbMsgCallback tbMsgCallback = Mockito.mock(TbMsgCallback.class); Mockito.when(tbMsgCallback.isMsgValid()).thenReturn(true); - TbMsg tbMsg = TbMsg.builder() + TbMsg tbMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(device.getId()) .metaData(TbMsgMetaData.EMPTY.copy()) 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 ad9e6d8d87..eed0734e6c 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,7 @@ public void testPushMsgToRuleEngineWithTenantIdIsNullUuidAndEntityIsTenantUseQue TbQueueCallback callback = mock(TbQueueCallback.class); TenantId tenantId = TenantId.fromUUID(UUID.fromString("3c8bd350-1239-4a3b-b9c3-4dd76f8e20f1")); - TbMsg requestMsg = TbMsg.builder() + TbMsg requestMsg = TbMsg.newMsg() .queueName(DataConstants.HP_QUEUE_NAME) .type(TbMsgType.REST_API_REQUEST) .originator(tenantId) @@ -311,7 +311,7 @@ public void testPushMsgToRuleEngineWithTenantIdIsNullUuidAndEntityIsTenantUseQue public void testPushMsgToRuleEngineWithTenantIdIsNullUuidAndEntityIsDevice() { TenantId tenantId = TenantId.SYS_TENANT_ID; DeviceId deviceId = new DeviceId(UUID.fromString("aa6d112d-2914-4a22-a9e3-bee33edbdb14")); - TbMsg requestMsg = TbMsg.builder() + TbMsg requestMsg = TbMsg.newMsg() .type(TbMsgType.REST_API_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -332,7 +332,7 @@ public void testPushMsgToRuleEngineWithTenantIdIsNotNullUuidUseQueueFromMsgIsTru 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.builder() + TbMsg requestMsg = TbMsg.newMsg() .queueName(DataConstants.HP_QUEUE_NAME) .type(TbMsgType.REST_API_REQUEST) .originator(deviceId) @@ -358,7 +358,7 @@ public void testPushMsgToRuleEngineUseQueueFromMsgIsFalse() { 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.builder() + TbMsg requestMsg = TbMsg.newMsg() .type(TbMsgType.REST_API_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) @@ -397,12 +397,12 @@ public void testGetRuleEngineProfileForUpdatedAndDeletedDevice() { device.setDeviceProfileId(deviceProfileId); // device updated - TbMsg tbMsg = TbMsg.builder().type(TbMsgType.ENTITY_UPDATED).build(); + TbMsg tbMsg = TbMsg.newMsg().type(TbMsgType.ENTITY_UPDATED).build(); ((DefaultTbClusterService) clusterService).getRuleEngineProfileForEntityOrElseNull(tenantId, deviceId, tbMsg); verify(deviceProfileCache, times(1)).get(tenantId, deviceId); // device deleted - tbMsg = TbMsg.builder().type(TbMsgType.ENTITY_DELETED).data(JacksonUtil.toString(device)).build(); + tbMsg = TbMsg.newMsg().type(TbMsgType.ENTITY_DELETED).data(JacksonUtil.toString(device)).build(); ((DefaultTbClusterService) clusterService).getRuleEngineProfileForEntityOrElseNull(tenantId, deviceId, tbMsg); verify(deviceProfileCache, times(1)).get(tenantId, deviceProfileId); } @@ -417,12 +417,12 @@ public void testGetRuleEngineProfileForUpdatedAndDeletedAsset() { asset.setAssetProfileId(assetProfileId); // asset updated - TbMsg tbMsg = TbMsg.builder().type(TbMsgType.ENTITY_UPDATED).build(); + TbMsg tbMsg = TbMsg.newMsg().type(TbMsgType.ENTITY_UPDATED).build(); ((DefaultTbClusterService) clusterService).getRuleEngineProfileForEntityOrElseNull(tenantId, assetId, tbMsg); verify(assetProfileCache, times(1)).get(tenantId, assetId); // asset deleted - tbMsg = TbMsg.builder().type(TbMsgType.ENTITY_DELETED).data(JacksonUtil.toString(asset)).build(); + tbMsg = TbMsg.newMsg().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 63f5f7999f..9a6db83f83 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,7 @@ public Set getPartitions() { } public void setUpTestMsg() { - testMsg = TbMsg.builder() + testMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(new DeviceId(UUID.randomUUID())) .metaData(new TbMsgMetaData().copy()) diff --git a/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineStrategyTest.java b/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineStrategyTest.java index 098c622e31..a24582099e 100644 --- a/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineStrategyTest.java +++ b/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineStrategyTest.java @@ -248,7 +248,7 @@ public void processMsgsTest(SubmitStrategyType submitStrategyType, ProcessingStr } private static TbMsg createRandomMsg() { - return TbMsg.builder() + return TbMsg.newMsg() .id(UUID.randomUUID()) .type("test type") .originator(deviceId) 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 ac103d54e3..9f849dacd7 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,7 @@ public void givenTbMsg_whenSendRestApiCallReply_thenPushNotificationToCore() { 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.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.REST_API_REQUEST) .originator(deviceId) .metaData(TbMsgMetaData.EMPTY.copy()) 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 48853c0e46..3196861020 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,7 @@ void givenRequest_whenProcessRestApiCallToRuleEngine_thenPushMsgToRuleEngineAndC metaData.put("serviceId", "core"); metaData.put("requestUUID", requestId.toString()); metaData.put("expirationTime", Long.toString(expTime)); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .queueName(DataConstants.MAIN_QUEUE_NAME) .type(TbMsgType.REST_API_REQUEST) .originator(TENANT_ID) @@ -119,7 +119,7 @@ void givenResponse_whenOnQueue_thenAcceptTbMsgResponse() { metaData.put("serviceId", "core"); metaData.put("requestUUID", requestId.toString()); metaData.put("expirationTime", Long.toString(expTime)); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .queueName(DataConstants.MAIN_QUEUE_NAME) .type(TbMsgType.REST_API_REQUEST) .originator(TENANT_ID) 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 5d06102a0b..cee0aa6428 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,7 +131,7 @@ Asset saveAsset(String name) throws Exception { void saveLatestTsForAssetAndDevice(List 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 int getAndIncrementRuleNodeCounter() { */ @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 boolean isTypeOneOf(TbMsgType... types) { 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 TbMsgBuilder toBuilder() { .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 TbMsgBuilder ts(long ts) { @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 void sendToRuleEngine(TbQueueProducer> p 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 @@ private void sendToRuleEngine(TenantId tenantId, DeviceId deviceId, CustomerId c 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 void testSimpleReplacement() { 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 void testNoReplacement() { 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 void testSameKeysReplacement() { 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 void testComplexObjectReplacement() { 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 void testArrayReplacementDoesNotWork() { 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 void onMsg(TbContext ctx, TbMsg msg) { 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 @@ private void processDeduplication(TbContext ctx, EntityId deduplicationId) { iterator.remove(); } } - deduplicationResults.add(TbMsg.builder() + deduplicationResults.add(TbMsg.newMsg() .queueName(queueName) .type(config.getOutMsgType()) .originator(deduplicationId) @@ -178,7 +178,7 @@ private void processDeduplication(TbContext ctx, EntityId deduplicationId) { } 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 void onMsg(TbContext ctx, TbMsg msg) { 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 @@ protected DeviceState getOrCreateDeviceState(TbContext ctx, DeviceId deviceId, R 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 @@ protected void updateProfile(TbContext ctx, DeviceProfileId deviceProfileId) thr } 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 @@ private void onDeviceUpdate(DeviceId deviceId, DeviceProfile deviceProfile) { 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 @@ private Customer createCustomer(String customerTitle) { } 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 @@ void before() { 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 @@ void alarmCanBeCleared() { 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 @@ void setUp() throws TbNodeException { 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 void givenExistingClientAttributes_whenOnMsg_thenCopyAttributesToView() { 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 void givenExistingServerAttributesAndMsgTypeAttributesDeleted_whenOnMsg_t 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 void givenNonMatchedSharedAttributesAndMsgTypeIsAttributesDeleted_whenOnM 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 void givenAttributesValidityPeriodOutOfStartDateAndEndDate_whenOnMsg_then ); 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 void givenAttributesValidityPeriodOutOfStartDateAndEndDate_whenOnMsg_then @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 @@ void whenAlarmDataIsTakenFromDefaultNodeConfigAndAlarmDoesNotExist_thenNewAlarmI 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 @@ void whenAlarmDataIsTakenFromNodeConfigAndClearedAlarmExists_thenNewAlarmIsCreat 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 @@ void whenAlarmDataIsTakenFromNodeConfigAndActiveAlarmExists_thenExistingAlarmIsU 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 @@ void whenAlarmDataIsTakenFromMsgAndClearedAlarmExists_thenNewAlarmIsCreated() th 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 @@ void whenAlarmDataIsTakenFromMsgAndActiveAlarmExists_thenExistingAlarmIsUpdated( .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 @@ void whenOnlySeverityWasUpdated_thenShouldTakeAlarmUpdatedPath() throws Exceptio .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 @@ void whenAlarmDetailsScriptThrowsException_thenShouldTellFailureAndNoOtherAction // 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 @@ void givenSupportedEntityType_whenOnMsg_thenVerifyRelationAndEntityCreatedAndOut 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 @@ private Map mockEntityServiceCallsEntityNotFound() { } 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 @@ private Map> verifyEntityServiceCalls() { } 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 void setup() { 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 EntityType getEntityType() { 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 void givenMetadataDoesNotContainTs_whenOnMsg_thenMsgTsIsUsedAsEventTs() { 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 @@ void givenMsg_whenToLog_thenReturnString() { 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 @@ void givenMsg_whenToLog_thenReturnString() { 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 @@ void givenEmptyDataMsg_whenToLog_thenReturnString() { 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 void givenIncomingMsgs_whenOnMsg_thenSendsMsgWithMsgCount() throws TbNode 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 void givenIncomingMsgs_whenOnMsg_thenSendsMsgWithMsgCount() throws TbNode 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 void givenInvalidMessageStructure_whenOnMsg_thenThrowsException() throws 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 void givenDataKeyIsMissingInMsg_whenOnMsg_thenThrowsException() throws Tb "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 void givenUnsupportedData_whenOnMsg_thenThrowsException() throws TbNodeEx "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 void givenTtl_whenOnMsg_thenVerifyStatement(int ttlFromConfig, 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 void givenValidMsgStructure_whenOnMsg_thenVerifyMatchOfValuesInsertionOrd } } """; - 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 @@ private Customer createCustomer(String customerTitle) { } 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 void givenRequest_whenOnMsg_thenTellSuccess(String data, TbMsgMetaData me 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 void givenExceptionWasThrownInsideFunctionAndTellFailureIfFuncThrowsExcIs 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 void givenExceptionWasThrownInsideFunctionAndTellFailureIfFuncThrowsExcIs 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 void givenExceptionWasThrownInsideFunctionAndTellFailureIfFuncThrowsExcIs 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 void givenPayloadFromResultIsNull_whenOnMsg_thenTellFailure() { @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 @@ void givenForceAckIsTrueAndTopicNamePattern_whenOnMsg_thenEnqueueForTellNext(Str 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 @@ void givenForceAckIsFalseAndErrorOccursDuringProcessingRequest_whenOnMsg_thenTel 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 @@ void givenQueueUrlPatternsAndQueueTypeIsFifo_whenOnMsg_thenVerifyRequest(String mockSendingMsgRequest(); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(metaData.copy()) @@ -148,7 +148,7 @@ void givenMsgAttributesPatternsAndQueueTypeIsStandard_whenOnMsg_thenVerifyReques mockSendingMsgRequest(); - TbMsg msg = TbMsg.builder() + TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) .metaData(metaData.copy()) @@ -196,7 +196,7 @@ void givenForceAckIsTrueAndMsgResultContainsBodyAndAttributesAndNumber_whenOnMsg 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 @@ void givenForceAckIsFalseAndErrorOccursDuringProcessingRequest_whenOnMsg_thenTel 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 void givenMsgCountAndDelay_whenInit_thenVerifyInvocationOfOnMsgMethod() t 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 void givenMsgCountAndDelay_whenInit_thenVerifyInvocationOfOnMsgMethod() t }).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 void givenMsgCountAndDelay_whenInit_thenVerifyInvocationOfOnMsgMethod() t // 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 void givenMsgCountAndDelay_whenInit_thenVerifyInvocationOfOnMsgMethod() t 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 void ackMsgInCaseNoEdgeRelated() { 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 void testAttributeUpdateMsg_userEntity() { 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 @@ private void testEvent(TbMsgType event, TbMsgMetaData metaData, EdgeEventActionT 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 @@ void givenMsg_whenOnMsg_then_Success() throws TbNodeException { } 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 @@ void givenUnparseableAlarm_whenOnMsg_then_Failure() { } 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 @@ 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 @@ private TbMsg getTbMsg(boolean emptyData) { 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 @@ void givenMsg_whenOnMsg_then_Success() throws TbNodeException { } 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 void falseEvaluationDoNotSendMsg() throws TbNodeException { 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 void exceptionInJsThrowsException() throws TbNodeException { 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 void multipleRoutesAreAllowed() throws TbNodeException { 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 @@ void givenAttributesUpdated_whenOnMsg_then_False() { } 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 @@ void givenAllTypes_whenOnMsg_then_allTypesSupported() throws TbNodeException { } 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 @@ void givenAsset_whenOnMsg_then_False() { } 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 @@ void givenAllTypes_whenOnMsg_then_allTypesSupported() throws TbNodeException { } 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 void givenDefaultConfig_whenInit_thenOk() { 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 void givenQueueName_whenOnMsg_thenTransfersMsgToDefinedQueue(String queue 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 void givenErrorDuringTransfer_whenOnMsg_thenTellFailure() throws TbNodeEx 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 @@ protected TbNode getTestNode() { } 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 void givenRuleNodeName_whenOnMsg_thenForwardMsgToTheCallerRuleChainWithRe 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 @@ public void givenForceAckIsTrueAndMessageAttributesPatterns_whenOnMsg_thenEnqueu 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 @@ public void givenForceAckIsFalse_whenOnMsg_thenTellSuccess() throws IOException, 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 @@ public void givenForceAckIsFalseAndErrorOccursOnTheGCP_whenOnMsg_thenTellFailure 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 @@ public void givenForceAckIsTrueAndErrorOccursOnTheGCP_whenOnMsg_thenEnqueueForTe 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 @@ private TbMsg getInsideRectangleTbMsg(EntityId entityId) { 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 @@ private TbMsgMetaData getMetadataForNewVersionCirclePerimeter() { 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 @@ private TbMsg getTbMsg(EntityId entityId, TbMsgMetaData metadata, double latitud } 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 void givenInitErrorIsNotNull_whenOnMsg_thenTellFailure() { 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 void givenForceAckAndExceptionWasThrown_whenOnMsg_thenTellFailure(boolean // 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 void givenForceAckIsTrueTopicAndKeyPatternsAndAddMetadataKeyValuesAsKafka // 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 void givenForceAckIsFalseAndKeyIsNullOrEmptyAndErrorOccursDuringPublishin // 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 void givenForceAckIsTrueAndAddKafkaHeadersIsTrueAndToBytesCharsetIsNullAn // 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 void givenForceAckIsFalseAndAddMetadataKeyValuesAsKafkaHeadersIsTrueAndTo 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 void givenMailBodyTypeTestConfig_whenOnMsg_thenVerify(MailBodyTypeTestCon } 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 void testExp4j() { 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 void testExp4j() { 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 void testSimpleTwoArgumentFunction(TbRuleNodeMathFunctionType function, d 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 void testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType function, d 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 void test_2_plus_2_body() { 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 void test_2_plus_2_meta() { 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 void test_2_plus_2_attr_and_ts() { 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 void test_sqrt_5_body() { 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 void test_sqrt_5_meta() { 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 void test_sqrt_5_to_attribute_and_metadata() { 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 void test_sqrt_5_to_timeseries_and_data() { 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 void test_sqrt_5_to_timeseries_and_metadata_and_data() { 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 void test_sqrt_5_default_value() { 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 void test_sqrt_5_default_value_failure() { 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 void testConvertMsgBodyIfRequiredFailure() { 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 void testExp4j_concurrent() { 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 void testExp4j_concurrent() { .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 void testExp4j_concurrentBySingleOriginator_processMsgAsyncException() { 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 void testExp4j_concurrentBySingleOriginator_SingleMsg_manyNodesWithDiffer }) .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 void givenInvalidMsgType_whenOnMsg_thenShouldTellNextOther() throws TbNod // 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 void givenInvalidMsgType_whenOnMsg_thenShouldTellNextOther() throws TbNod 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 void givenInvalidMsgDataType_whenOnMsg_thenShouldTellNextOther() throws T 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 void givenDoubleValue_whenOnMsgAndCachingOff_thenShouldTellSuccess() thro 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 void givenLongStringValue_whenOnMsgAndCachingOff_thenShouldTellSuccess() 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 void givenValidStringValue_whenOnMsgAndCachingOff_thenShouldTellSuccess() 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 void givenTwoMessagesAndPeriodOnAndCachingOn_whenOnMsg_thenVerify() throw 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 void givenTwoMessagesAndPeriodOnAndCachingOn_whenOnMsg_thenVerify() throw 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 void givenLastValueIsNull_whenOnMsgAndCachingOff_thenDeltaShouldBeZero() 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 void givenNegativeDeltaAndTellFailureIfNegativeDeltaTrue_whenOnMsg_thenSh 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 void givenNegativeDeltaAndTellFailureIfNegativeDeltaFalse_whenOnMsg_thenS 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 void givenInvalidStringValue_whenOnMsg_thenException() throws TbNodeExcep 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 void givenBooleanValue_whenOnMsg_thenException() throws TbNodeException { 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 void givenJsonValue_whenOnMsg_thenException() throws TbNodeException { 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 void givenConcurrentAccess_whenOnMsg_thenGetFromDBInvokedOnce() throws Tb 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 void givenCalculateDeltaConfig_whenOnMsg_thenVerify(CalculateDeltaTestCon 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 @@ private TbMsg getTbMsg(EntityId entityId) { 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 void givenFetchLatestTimeseriesToData_whenOnMsg_thenShouldTellFailure() t 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 @@ private TbMsg getTbMsg(EntityId entityId) { 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 void givenEmptyAttributesMapping_whenInit_thenException() { 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 void givenDidNotFindEntity_whenOnMsg_thenShouldTellFailure() { // 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 @@ private void prepareMsgAndConfig(TbMsgSource fetchTo, DataToFetch dataToFetch, E 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 void givenCustomConfig_whenInit_thenOK() throws TbNodeException { 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 @@ private void prepareMsgAndConfig(TbMsgSource fetchTo, List { 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 void whenAttributeIsDeleted_thenUnneededAlarmRulesAreNotReevaluated() thr 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 void whenAttributeIsDeleted_thenUnneededAlarmRulesAreNotReevaluated() thr 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 void whenAttributeIsDeleted_thenUnneededAlarmRulesAreNotReevaluated() thr 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 void whenDeletingClearedAlarm_thenNoError() throws Exception { 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 void whenDeletingClearedAlarm_thenNoError() throws Exception { 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 void testRandomMessageType() throws Exception { 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 void testEmptyProfile() throws Exception { 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 void testAlarmCreate() throws Exception { 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 void testAlarmCreate() throws Exception { 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 void testAlarmCreate() throws Exception { 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 void testAlarmCreate() throws Exception { 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 void testAlarmSeverityUpdate() throws Exception { 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 void testAlarmSeverityUpdate() throws Exception { 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 void testAlarmSeverityUpdate() throws Exception { 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 void testAlarmSeverityUpdate() throws Exception { 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 void testConstantKeyFilterSimple() throws Exception { 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 void testConstantKeyFilterSimple() throws Exception { 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 void testConstantKeyFilterInherited() throws Exception { 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 void testConstantKeyFilterInherited() throws Exception { 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 void testCurrentDeviceAttributeForDynamicValue() throws Exception { 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 void testCurrentDeviceAttributeForDynamicValue() throws Exception { 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 void testCurrentDeviceAttributeForDynamicDurationValue() throws Exception 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 void testCurrentDeviceAttributeForDynamicDurationValue() throws Exception 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 void testCurrentDeviceAttributeForDynamicDurationValue() throws Exception 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 void testInheritTenantAttributeForDuration() throws Exception { 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 void testInheritTenantAttributeForDuration() throws Exception { 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 void testInheritTenantAttributeForDuration() throws Exception { 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 void testCurrentDeviceAttributeForDynamicRepeatingValue() throws Exceptio 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 void testCurrentDeviceAttributeForDynamicRepeatingValue() throws Exceptio 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 void testCurrentDeviceAttributeForDynamicRepeatingValue() throws Exceptio 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 void testInheritTenantAttributeForRepeating() throws Exception { 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 void testInheritTenantAttributeForRepeating() throws Exception { 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 void testInheritTenantAttributeForRepeating() throws Exception { 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 void testCurrentDeviceAttributeForUseDefaultDurationWhenDynamicDurationVa 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 void testCurrentDeviceAttributeForUseDefaultDurationWhenDynamicDurationVa 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 void testCurrentDeviceAttributeForUseDefaultDurationWhenDynamicDurationVa 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 void testCurrentDeviceAttributeForUseDefaultRepeatingWhenDynamicDurationV 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 void testCurrentDeviceAttributeForUseDefaultRepeatingWhenDynamicDurationV 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 void testActiveAlarmScheduleFromDynamicValuesWhenDefaultScheduleIsInactiv 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 void testActiveAlarmScheduleFromDynamicValuesWhenDefaultScheduleIsInactiv 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 void testInactiveAlarmScheduleFromDynamicValuesWhenDefaultScheduleIsActiv 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 void testInactiveAlarmScheduleFromDynamicValuesWhenDefaultScheduleIsActiv 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 void testCurrentCustomersAttributeForDynamicValue() throws Exception { 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 void testCurrentCustomersAttributeForDynamicValue() throws Exception { 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 void testCurrentTenantAttributeForDynamicValue() throws Exception { 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 void testCurrentTenantAttributeForDynamicValue() throws Exception { 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 void testTenantInheritModeForDynamicValues() throws Exception { 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 void testTenantInheritModeForDynamicValues() throws Exception { 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 void testCustomerInheritModeForDynamicValues() throws Exception { 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 void testCustomerInheritModeForDynamicValues() throws Exception { 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 void givenForceAckIsTrueAndExchangeNameAndRoutingKeyPatternsAndBasicPrope 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 void givenForceAckIsFalseAndExchangeNameAndRoutingKeyPatternsAndBasicProp 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 void givenForceAckAndErrorOccursDuringPublishing_whenOnMsg_thenVerifyTell 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 void testProcessMessageWithJsonInUrlVariable() throws Exception { 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 void run() { 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 void run() { 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 void givenValidRestApiRequest_whenOnMsg_thenTellSuccess(String requestIdA 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 @@ private static Stream givenValidRestApiRequest_whenOnMsg_thenTellSucc @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 void setUp() throws TbNodeException { 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 void sendReplyToEdgeQueue() { 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 void sendReplyToEdgeQueue() { @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 void testOriginatorEntityTypes(EntityType entityType) { @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 void givenOneway_whenOnMsg_thenVerifyRequest(String mdKeyValue, boolean e 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 void givenMsgBody_whenOnMsg_thenVerifyRequest() { 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 void givenRequestIdIsNotSet_whenOnMsg_thenVerifyRequest() { 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 void givenRequestId_whenOnMsg_thenVerifyRequest() { "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 void givenRequestUUID_whenOnMsg_thenVerifyRequest() { 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 void givenInvalidRequestUUID_whenOnMsg_thenVerifyRequest(String requestUU 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 void givenOriginServiceId_whenOnMsg_thenVerifyRequest() { 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 void givenInvalidOriginServiceId_whenOnMsg_thenVerifyRequest(String origi 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 void givenExpirationTime_whenOnMsg_thenVerifyRequest() { 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 void givenInvalidExpirationTime_whenOnMsg_thenVerifyRequest(String expira 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 void givenRetries_whenOnMsg_thenVerifyRequest() { 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 void givenInvalidRetriesValue_whenOnMsg_thenVerifyRequest(String retries) 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 void givenTbMsgType_whenOnMsg_thenVerifyRequest(TbMsgType msgType) { 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 void givenPersistent_whenOnMsg_thenVerifyRequest(String isPersisted, bool 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 @@ private ArgumentCaptor captureRequest() { @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 void givenRpcResponseWithoutError_whenOnMsg_thenSendsRpcRequest() { 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 void givenRpcResponseWithoutError_whenOnMsg_thenSendsRpcRequest() { @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 void givenRpcResponseWithError_whenOnMsg_thenTellFailure() { 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 void givenRpcResponseWithError_whenOnMsg_thenTellFailure() { 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 void givenOriginatorIsNotDevice_whenOnMsg_thenThrowsException(EntityType @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 @@ void givenNotifyDeviceMdValue_whenSaveAndNotify_thenVerifyExpectedArgumentForNot 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 @@ void onMsg_thenVerifyOutput(boolean sendAttributesDeletedNotification, boolean n } 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 void verifyDefaultConfig() { @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 void givenTtlFromConfigIsZeroAndUseServiceTsIsTrue_whenOnMsg_thenSaveTime "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 void givenSkipLatestPersistenceIsTrueAndTtlFromConfig_whenOnMsg_thenSaveT """; 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 void givenTtlFromConfigAndTtlFromMd_whenOnMsg_thenVerifyTtl(String ttlFro """; 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 void givenOriginatorSourceIsCustomer_whenOnMsg_thenTellSuccess() throws T 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 void givenOriginatorSourceIsCustomer_whenOnMsg_thenTellSuccess() throws T 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 void givenOriginatorSourceIsTenant_whenOnMsg_thenTellSuccess() throws TbN 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 void givenOriginatorSourceIsAlarmOriginator_whenOnMsg_thenTellSuccess() t 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 void givenOriginatorSourceIsEntity_whenOnMsg_thenTellSuccess(String entit 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 void givenOriginatorSourceIsEntityAndEntityCouldNotFound_whenOnMsg_thenTe 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 @@ private TbMsg getTbMsg(EntityId entityId, String data) { "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 @@ private TbMsg getTbMsg(EntityId entityId, String data) { "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 @@ private TbMsg getTbMsg(EntityId entityId, String data) { 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 void init() throws TbNodeException { 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 @@ private TbMsg createMsg(DeviceId deviceId, long ts) { 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 @@ private TbMsg getTbMsg(EntityId entityId, String data) { "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 @@ private TbMsg getTbMsg(EntityId entityId, String data) { "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 void metadataCanBeUpdated() throws TbNodeException { 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 void metadataCanBeUpdated() throws TbNodeException { .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 void exceptionHandledCorrectly() throws TbNodeException { 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 06/40] 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 TbMsg newMsg(String queueName, String type, EntityId originator, Customer @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 TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, Custo @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 @@ private TbMsg transformMsg(TbMsg tbMsg, HasRuleEngineProfile ruleEngineProfile, 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 @@ private static TbMsg unbindMsg(JsonNode msgData, TbMsg msg) { String newData = data != null ? data : msg.getData(); TbMsgMetaData newMetadata = metadata != null ? new TbMsgMetaData(metadata) : msg.getMetaData().copy(); String newMessageType = !StringUtils.isEmpty(messageType) ? messageType : msg.getType(); - return TbMsg.transformMsg(msg, newMessageType, msg.getOriginator(), newMetadata, newData); + return msg.transform() + .type(newMessageType) + .metaData(newMetadata) + .data(newData) + .build(); } } diff --git a/application/src/main/java/org/thingsboard/server/service/script/RuleNodeTbelScriptEngine.java b/application/src/main/java/org/thingsboard/server/service/script/RuleNodeTbelScriptEngine.java index 39b2b3bc07..25f29a3374 100644 --- a/application/src/main/java/org/thingsboard/server/service/script/RuleNodeTbelScriptEngine.java +++ b/application/src/main/java/org/thingsboard/server/service/script/RuleNodeTbelScriptEngine.java @@ -156,7 +156,11 @@ private static TbMsg unbindMsg(Map msgData, TbMsg msg) { String newData = data != null ? data : msg.getData(); TbMsgMetaData newMetadata = metadata != null ? new TbMsgMetaData(metadata) : msg.getMetaData().copy(); String newMessageType = !StringUtils.isEmpty(messageType) ? messageType : msg.getType(); - return TbMsg.transformMsg(msg, newMessageType, msg.getOriginator(), newMetadata, newData); + return msg.transform() + .type(newMessageType) + .metaData(newMetadata) + .data(newData) + .build(); } private static 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 void testPushMsgToRuleEngineUseQueueFromMsgIsFalse() { 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 int getAndIncrementRuleNodeCounter() { 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 static TbMsg fromBytes(String queueName, byte[] data, TbMsgCallback callb } } - 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 static TbMsgBuilder newMsg() { } 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 TbMsgBuilder type(String type) { 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 @@ protected ListenableFuture buildAlarmDetails(TbMsg msg, JsonNode previ if (previousDetails != null) { TbMsgMetaData metaData = msg.getMetaData().copy(); metaData.putValue(PREV_ALARM_DETAILS, JacksonUtil.toString(previousDetails)); - dummyMsg = TbMsg.transformMsgMetadata(msg, metaData); + dummyMsg = msg.transform() + .metaData(metaData) + .build(); } return scriptEngine.executeJsonAsync(dummyMsg); } catch (Exception e) { 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 1dbc81bfa1..bb9f5416db 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 @@ -132,14 +132,19 @@ private TbMsg getResponseMsg(TbMsg originalMsg, InvokeResult invokeResult) { TbMsgMetaData metaData = originalMsg.getMetaData().copy(); metaData.putValue("requestId", invokeResult.getSdkResponseMetadata().getRequestId()); String data = getPayload(invokeResult); - return TbMsg.transformMsg(originalMsg, metaData, data); + return originalMsg.transform() + .metaData(metaData) + .data(data) + .build(); } private TbMsg processException(TbMsg origMsg, InvokeResult invokeResult, Throwable t) { TbMsgMetaData metaData = origMsg.getMetaData().copy(); metaData.putValue("error", t.getClass() + ": " + t.getMessage()); metaData.putValue("requestId", invokeResult.getSdkResponseMetadata().getRequestId()); - 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/aws/sns/TbSnsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sns/TbSnsNode.java index 6516a5e4b3..2c732150e1 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 @@ -103,13 +103,17 @@ private TbMsg processPublishResult(TbMsg origMsg, PublishResult result) { TbMsgMetaData metaData = origMsg.getMetaData().copy(); metaData.putValue(MESSAGE_ID, result.getMessageId()); metaData.putValue(REQUEST_ID, result.getSdkResponseMetadata().getRequestId()); - return TbMsg.transformMsgMetadata(origMsg, metaData); + return origMsg.transform() + .metaData(metaData) + .build(); } 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/aws/sqs/TbSqsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNode.java index 4dbc188cdd..e61cd537c4 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 @@ -134,13 +134,17 @@ private TbMsg processSendMessageResult(TbMsg origMsg, SendMessageResult result) if (!StringUtils.isEmpty(result.getSequenceNumber())) { metaData.putValue(SEQUENCE_NUMBER, result.getSequenceNumber()); } - return TbMsg.transformMsgMetadata(origMsg, metaData); + return origMsg.transform() + .metaData(metaData) + .build(); } 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/gcp/pubsub/TbPubSubNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java index 537318fa75..d6d3bb3c11 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 @@ -120,13 +120,17 @@ public void onFailure(Throwable t) { private TbMsg processPublishResult(TbMsg origMsg, String messageId) { TbMsgMetaData metaData = origMsg.getMetaData().copy(); metaData.putValue(MESSAGE_ID, messageId); - return TbMsg.transformMsgMetadata(origMsg, metaData); + return origMsg.transform() + .metaData(metaData) + .build(); } 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(); } Publisher initPubSubClient(TbContext ctx) throws IOException { 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 89b6e1c1d9..865eac0aac 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 @@ -188,13 +188,17 @@ private TbMsg processResponse(TbMsg origMsg, RecordMetadata recordMetadata) { metaData.putValue(OFFSET, String.valueOf(recordMetadata.offset())); metaData.putValue(PARTITION, String.valueOf(recordMetadata.partition())); metaData.putValue(TOPIC, recordMetadata.topic()); - return TbMsg.transformMsgMetadata(origMsg, metaData); + return origMsg.transform() + .metaData(metaData) + .build(); } private TbMsg processException(TbMsg origMsg, Exception e) { TbMsgMetaData metaData = origMsg.getMetaData().copy(); metaData.putValue(ERROR, e.getClass() + ": " + e.getMessage()); - return TbMsg.transformMsgMetadata(origMsg, metaData); + return origMsg.transform() + .metaData(metaData) + .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 4e64ad854e..2a552f534c 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 @@ -202,7 +202,9 @@ private TbMsg addToBody(TbMsg msg, TbMathResult mathResultDef, String mathResult } else { body.put(mathResultKey, toDoubleValue(mathResultDef, result)); } - return TbMsg.transformMsgData(msg, JacksonUtil.toString(body)); + return msg.transform() + .data(JacksonUtil.toString(body)) + .build(); } private TbMsg addToMeta(TbMsg msg, TbMathResult mathResultDef, String mathResultKey, double result) { @@ -212,7 +214,9 @@ private TbMsg addToMeta(TbMsg msg, TbMathResult mathResultDef, String mathResult } else { md.putValue(mathResultKey, Double.toString(toDoubleValue(mathResultDef, result))); } - return TbMsg.transformMsgMetadata(msg, md); + return msg.transform() + .metaData(md) + .build(); } private double calculateResult(List 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 @@ protected ListenableFuture processMsgAsync(TbContext ctx, TbMsg msg) { 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 @@ protected void enrichMessage(ObjectNode msgData, TbMsgMetaData metaData, KvEntry protected TbMsg transformMessage(TbMsg msg, ObjectNode msgDataNode, TbMsgMetaData msgMetaData) { switch (fetchTo) { case DATA: - return TbMsg.transformMsgData(msg, JacksonUtil.toString(msgDataNode)); + return msg.transform() + .data(JacksonUtil.toString(msgDataNode)) + .build(); case METADATA: - return TbMsg.transformMsgMetadata(msg, msgMetaData); + return msg.transform() + .metaData(msgMetaData) + .build(); default: log.debug("Unexpected FetchTo value: {}. Allowed values: {}", fetchTo, TbMsgSource.values()); return msg; 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 3a6f1dd9c4..8e6e64bb17 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 @@ -115,7 +115,9 @@ public void onMsg(TbContext ctx, TbMsg msg) { ListenableFuture> 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 void onMsg(TbContext ctx, TbMsg msg) { 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 void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, Interrupt 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 @@ private TbMsg publishMessage(TbContext ctx, TbMsg msg) throws Exception { 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 @@ private TbMsg processFailureResponse(TbMsg origMsg, ResponseEntity respo 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 @@ private TbMsg processException(TbMsg origMsg, Throwable e) { 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 void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, Interrupt 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 void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, Interrupt 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 void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, Interrupt 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 void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, Interrupt 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 void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, Interrupt 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 void onFailure(RuleEngineException e) { } }); 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 @@ void whenAlarmDataIsTakenFromDefaultNodeConfigAndAlarmDoesNotExist_thenNewAlarmI 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 @@ void whenAlarmDataIsTakenFromNodeConfigAndClearedAlarmExists_thenNewAlarmIsCreat 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 @@ void whenAlarmDataIsTakenFromNodeConfigAndActiveAlarmExists_thenExistingAlarmIsU 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 @@ void whenAlarmDataIsTakenFromMsgAndClearedAlarmExists_thenNewAlarmIsCreated() th 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 @@ void whenAlarmDataIsTakenFromMsgAndActiveAlarmExists_thenExistingAlarmIsUpdated( 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 @@ void whenOnlySeverityWasUpdated_thenShouldTakeAlarmUpdatedPath() throws Exceptio 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 @@ void givenSupportedEntityType_whenOnMsg_thenVerifyRelationCreatedAndOriginatorCh 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 void givenRequest_whenOnMsg_thenTellSuccess(String data, TbMsgMetaData me 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 void givenExceptionWasThrownInsideFunctionAndTellFailureIfFuncThrowsExcIs 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 void givenExceptionWasThrownInsideFunctionAndTellFailureIfFuncThrowsExcIs 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 void givenPayloadFromResultIsNull_whenOnMsg_thenTellFailure() { 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 @@ public void givenForceAckIsTrueAndMessageAttributesPatterns_whenOnMsg_thenEnqueu 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 @@ public void givenForceAckIsFalse_whenOnMsg_thenTellSuccess() throws IOException, 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 @@ public void givenForceAckIsFalseAndErrorOccursOnTheGCP_whenOnMsg_thenTellFailure 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 @@ public void givenForceAckIsTrueAndErrorOccursOnTheGCP_whenOnMsg_thenEnqueueForTe 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 @@ private void verifyOutgoingSuccessMsg(String expectedTopic, TbMsg actualMsg, TbM 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 @@ private void verifyOutgoingSuccessMsg(String expectedTopic, TbMsg actualMsg, TbM 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 void givenFetchModeAll_whenOnMsg_thenTellSuccessAndVerifyMsg() throws TbN 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 void givenFetchMode_whenOnMsg_thenTellSuccessAndVerifyMsg(String fetchMod 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 void givenForceAckIsFalseParseToPlainTextIsTrueAndMsgPublishingFailed_whe 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 void givenForceAckAndErrorOccursDuringPublishing_whenOnMsg_thenVerifyTell () -> 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 void givenOriginatorSourceIsCustomer_whenOnMsg_thenTellSuccess() throws T .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 void givenOriginatorSourceIsTenant_whenOnMsg_thenTellSuccess() throws TbN .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 void givenOriginatorSourceIsAlarmOriginator_whenOnMsg_thenTellSuccess() t .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 void givenOriginatorSourceIsEntity_whenOnMsg_thenTellSuccess(String entit .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 07/40] 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 void input(TbMsg msg, RuleChainId ruleChainId) { 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.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 @@ private void onTellNext(TbMsg msg, boolean useRuleNodeIdFromMsg) { RuleNodeCtx targetCtx; if (targetId == null) { targetCtx = firstNode; - msg = msg.copyWithRuleChainId(entityId); + msg = msg.copy() + .ruleChainId(entityId) + .ruleNodeId(null) + .build(); } else { targetCtx = nodeActors.get(targetId); } @@ -343,10 +346,18 @@ private void onTellNext(TbMsg msg, RuleNodeId originatorNodeId, Set rela private void putToQueue(TopicPartitionInfo tpi, TbMsg msg, TbQueueCallback callbackWrapper, EntityId target) { switch (target.getEntityType()) { case RULE_NODE: - putToQueue(tpi, msg.copyWithRuleNodeId(entityId, new RuleNodeId(target.getId()), UUID.randomUUID()), callbackWrapper); + putToQueue(tpi, msg.copy() + .id(UUID.randomUUID()) + .ruleChainId(entityId) + .ruleNodeId(new RuleNodeId(target.getId())) + .build(), callbackWrapper); break; case RULE_CHAIN: - putToQueue(tpi, msg.copyWithRuleChainId(new RuleChainId(target.getId()), UUID.randomUUID()), callbackWrapper); + putToQueue(tpi, msg.copy() + .id(UUID.randomUUID()) + .ruleChainId(new RuleChainId(target.getId())) + .ruleNodeId(null) + .build(), callbackWrapper); break; } } 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 f758aa8e8a..797ef3ce4f 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 @@ -19,7 +19,6 @@ import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; import lombok.AccessLevel; -import lombok.AllArgsConstructor; import lombok.Data; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -38,14 +37,11 @@ import java.util.Objects; import java.util.UUID; -import static java.util.Objects.requireNonNull; - /** * Created by ashvayka on 13.01.18. */ @Data @Slf4j -@AllArgsConstructor(access = AccessLevel.PRIVATE) public final class TbMsg implements Serializable { public static final String EMPTY_JSON_OBJECT = "{}"; @@ -77,8 +73,16 @@ public final class TbMsg implements Serializable { @JsonIgnore transient private final TbMsgCallback callback; - public int getAndIncrementRuleNodeCounter() { - return ctx.getAndIncrementRuleNodeCounter(); + public static TbMsgBuilder newMsg() { + return new TbMsgBuilder(); + } + + public TbMsgBuilder transform() { + return new TbMsgTransformer(this); + } + + public TbMsgBuilder copy() { + return new TbMsgBuilder(this); } public TbMsg transform(String queueName) { @@ -100,23 +104,11 @@ public static TbMsg newMsg(TbMsg tbMsg, String queueName, RuleChainId ruleChainI .build(); } - 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); + return copy() + .ctx(ctx.copy()) + .callback(TbMsgCallback.EMPTY) + .build(); } private TbMsg(String queueName, UUID id, long ts, TbMsgType internalType, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data, @@ -142,7 +134,7 @@ private TbMsg(String queueName, UUID id, long ts, TbMsgType internalType, String } this.metaData = metaData; this.dataType = dataType != null ? dataType : TbMsgDataType.JSON; - this.data = requireNonNull(data, "msg data is missing"); + this.data = data; this.ruleChainId = ruleChainId; this.ruleNodeId = ruleNodeId; this.correlationId = correlationId; @@ -238,6 +230,10 @@ public static TbMsg fromBytes(String queueName, byte[] data, TbMsgCallback callb } } + public int getAndIncrementRuleNodeCounter() { + return ctx.getAndIncrementRuleNodeCounter(); + } + public TbMsgCallback getCallback() { // May be null in case of deserialization; return Objects.requireNonNullElse(callback, TbMsgCallback.EMPTY); @@ -293,19 +289,7 @@ public boolean isTypeOneOf(TbMsgType... types) { return false; } - public static TbMsgBuilder newMsg() { - return new TbMsgBuilder(); - } - - public TbMsgBuilder transform() { - return new TbMsgTransformer(this); - } - - public TbMsgBuilder copy() { - return new TbMsgBuilder(this); - } - - private static class TbMsgTransformer extends TbMsgBuilder { + public static class TbMsgTransformer extends TbMsgBuilder { TbMsgTransformer(TbMsg tbMsg) { super(tbMsg); @@ -335,8 +319,8 @@ public TbMsg build() { /* * always copying ctx when transforming * */ - if (ctx != null) { - ctx = ctx.copy(); + if (this.ctx != null) { + this.ctx = this.ctx.copy(); } return super.build(); } @@ -472,7 +456,7 @@ public TbMsgBuilder callback(TbMsgCallback callback) { } public TbMsg build() { - return new TbMsg(queueName, id, ts, type, internalType, originator, customerId, metaData, dataType, data, ruleChainId, ruleNodeId, correlationId, partition, ctx, callback); + return new TbMsg(queueName, id, ts, internalType, type, originator, customerId, metaData, dataType, data, ruleChainId, ruleNodeId, correlationId, partition, ctx, callback); } public String toString() { From 2bb65923dc416959e254e14b57746f2c40772e5a Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 16 Dec 2024 16:40:06 +0200 Subject: [PATCH 08/40] 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.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 DeferredResult saveEntityAttributesV1( 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 DeferredResult saveEntityAttributesV2( 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 DeferredResult saveEntityTelemetry( 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 void onFailure(Throwable t) { @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 DeferredResult deleteDeviceAttributes( 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 @@ private DeferredResult deleteAttributes(EntityId entityIdSrc, At 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 @@ private DeferredResult saveTelemetry(TenantId curTenantId, Entit 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.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 @@ private void onEdgeDisconnect(Edge edge, UUID sessionId) { 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 @@ private void save(TenantId tenantId, EdgeId edgeId, String key, long value) { 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 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 @@ private void updateFirmware(Device device, Device oldDevice) { // 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 @@ private void updateSoftware(Device device, Device oldDevice) { // 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 @@ private void send(TenantId tenantId, DeviceId deviceId, OtaPackageId firmwareId, 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 @@ private void update(Device device, OtaPackageInfo otaPackage, long ts) { 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 void onFailure(Throwable 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 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.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 @@ private void saveTelemetry(SecurityUser user, E entity, Map.Entry { 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.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 @@ */ @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 void shutdownExecutor() { } @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 void saveLatestAndNotifyInternal(TenantId tenantId, EntityId entityId, Li 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 void deleteTimeseriesAndNotify(TenantId tenantId, EntityId entityId, List 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 void saveAttrAndNotify(TenantId tenantId, EntityId entityId, AttributeSco , 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 ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId enti 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 ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId enti 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 ListenableFuture saveAttrAndNotify(TenantId tenantId, EntityId enti 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 void onSuccess(Void result) { 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.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 void testEntityCountCmd_filterTypeSingularCompatibilityTest() throws Exce 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 void onFailure(Throwable t) { }); 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 @@ 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 @@ */ 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.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 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 @@ private ListenableFuture updateMsgAndDb(TbContext ctx, TbMsg msg, Optiona } 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.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 void onMsg(TbContext ctx, TbMsg msg) { 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.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.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 void test_sqrt_5_to_timeseries_and_data() { ); 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 void test_sqrt_5_to_timeseries_and_metadata_and_data() { ); 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.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 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 void givenTtlFromConfigIsZeroAndUseServiceTsIsTrue_whenOnMsg_thenSaveTime 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 void givenSkipLatestPersistenceIsTrueAndTtlFromConfig_whenOnMsg_thenSaveT 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 void givenTtlFromConfigAndTtlFromMd_whenOnMsg_thenVerifyTtl(String ttlFro 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 09/40] 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 @@ private DeferredResult saveTelemetry(TenantId curTenantId, Entit .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.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 @@ 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 @@ private void processEntityUsageStats(TenantId tenantId, EntityId ownerId, 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 @@ private void saveNewCounts(BaseApiUsageState state, List keys .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 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 @@ private void save(TenantId tenantId, EdgeId edgeId, String key, long value) { 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 @@ private void save(TenantId tenantId, EdgeId edgeId, String key, boolean value) { 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.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 @@ private void send(TenantId tenantId, DeviceId deviceId, OtaPackageId firmwareId, .tenantId(tenantId) .entityId(deviceId) .entries(telemetry) - .saveLatest(true) .callback(new FutureCallback() { @Override public void onSuccess(@Nullable Void tmp) { @@ -292,9 +290,8 @@ private void update(Device device, OtaPackageInfo otaPackage, long ts) { 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.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 @@ private void pushRuleEngineMessage(DeviceStateData stateData, TbMsgType msgType) private void save(DeviceId deviceId, String key, long value) { if (persistToTelemetry) { - tsSubService.saveAndNotifyInternal( - TenantId.SYS_TENANT_ID, deviceId, - Collections.singletonList(new BasicTsKvEntry(getCurrentTimeMillis(), new LongDataEntry(key, value))), - telemetryTtl, new TelemetrySaveCallback<>(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 @@ private void save(DeviceId deviceId, String key, long value) { private void save(DeviceId deviceId, String key, boolean value) { if (persistToTelemetry) { - tsSubService.saveAndNotifyInternal( - TenantId.SYS_TENANT_ID, deviceId, - Collections.singletonList(new BasicTsKvEntry(getCurrentTimeMillis(), new BooleanDataEntry(key, value))), - telemetryTtl, new TelemetrySaveCallback<>(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.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.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 @@ 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 void reportQueueStats(long ts, TbRuleEngineConsumerStats ruleEngineStats) 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 void reportQueueStats(long ts, TbRuleEngineConsumerStats ruleEngineStats) 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 @@ private void saveTelemetry(SecurityUser user, E entity, Map.Entry() { + .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.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 @@ @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 @@ private void saveCurrentMonolithSystemInfo() { private void doSave(List 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 void shutdownExecutor() { 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 void save(TimeseriesSaveRequest request) { 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 void onFailure(Throwable t) { }, 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 @@ private void checkInternalEntity(EntityId entityId) { } } + 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 @@ */ 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 @@ private void sendTelemetry(Device device, List tsData) throws Interru 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 @@ 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 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 static class Builder { 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 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 @@ private ListenableFuture saveTimeSeries(TbContext ctx, TbMsg msg, double r 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 10/40] 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 11/40] 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.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 @@ private DeferredResult saveAttributes(TenantId srcTenantId, Enti } 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.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.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 ListenableFuture reClaimDevice(TenantId tenantId, Device d 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 void onSuccess(@Nullable Void tmp) { 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.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.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 @@ private void save(TenantId tenantId, EdgeId edgeId, String key, long value) { 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 @@ private void save(TenantId tenantId, EdgeId edgeId, String key, boolean value) { 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.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 @@ private ListenableFuture processAttributesUpdate(TenantId tenantId, 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 void onFailure(Throwable t) { 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.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 @@ private ListenableFuture> copyAttributesFromEntityToEntityView(Entity 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 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 @@ private void updateAttributes(Device device, OtaPackageInfo otaPackage, long ts, 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.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.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 @@ private void pushRuleEngineMessage(DeviceStateData stateData, TbMsgType msgType) } private void save(DeviceId deviceId, String key, long value) { - if (persistToTelemetry) { - 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)); - } + 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 @@ long getCurrentTimeMillis() { private static class TelemetrySaveCallback 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.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 @@ private void saveAttributes(SecurityUser user, E entity, Map.Entry 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.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 @@ private void importAttributes(User user, Map> }) .collect(Collectors.toList()); // fixme: attributes are saved outside the transaction - tsSubService.saveAndNotify(user.getTenantId(), entity.getId(), scope, attributeKvEntries, new FutureCallback() { - @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.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.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 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 ListenableFuture saveInternal(TimeseriesSaveRequest request) { 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 void deleteTimeseriesAndNotify(TenantId tenantId, EntityId entityId, List 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 @@ 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.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 @@ private void sendAttributes(Device device, TbAttributeSubscriptionScope scope, L 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.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.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.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 void givenDeviceBelongsToMyPartition_whenOnDeviceConnect_thenReportsConne 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 void givenDeviceBelongsToMyPartition_whenOnDeviceDisconnect_thenReportsDi 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 void givenDeviceBelongsToMyPartition_whenOnDeviceInactivity_thenReportsIn 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 void givenInactivityTimeoutReached_whenUpdateInactivityStateIfExpired_the 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 void increaseInactivityForActiveDeviceTest() throws Exception { 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 void increaseSmallInactivityForInactiveDeviceTest() throws Exception { 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 void decreaseInactivityForActiveDeviceTest() throws Exception { 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 void decreaseInactivityForInactiveDeviceTest() throws Exception { 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 void givenTestParameters_whenUpdateActivityState_thenShouldBeInTheExpecte // 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 void givenTestParameters_whenUpdateActivityState_thenShouldBeInTheExpecte 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 void givenTestParameters_whenOnDeviceInactivityTimeout_thenShouldBeInTheE 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 void givenTestParameters_whenUpdateInactivityStateIfExpired_thenShouldBeI 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 void givenTestParameters_whenUpdateInactivityStateIfExpired_thenShouldBeI 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 void givenDeviceAdded_whenOnQueueMsg_thenShouldCacheAndSaveActivityToFals // 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 void givenDeviceActivityEventHappenedAfterAdded_whenOnDeviceActivity_then 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 void givenDeviceActivityEventHappenedBeforeAdded_whenOnQueueMsg_thenShoul // 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 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 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 Builder entries(List entries) { } 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 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 void onMsg(TbContext ctx, TbMsg msg) { 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.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.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 @@ private ListenableFuture saveTimeSeries(TbContext ctx, TbMsg msg, double r 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 @@ 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 @@ 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 @@ void saveAttr(List attributes, TbContext ctx, TbMsg msg, Attri 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.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.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 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 void givenExistingClientAttributes_whenOnMsg_thenCopyAttributesToView() { 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 void givenExistingClientAttributes_whenOnMsg_thenCopyAttributesToView() { 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 @@ TbMsgType.POST_ATTRIBUTES_REQUEST, DEVICE_ID, new TbMsgMetaData(Map.of(DataConst 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.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.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 void test_sqrt_5_to_attribute_and_metadata() { 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 void testConvertMsgBodyIfRequiredFailure() { 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.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 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 @@ void givenNotifyDeviceMdValue_whenSaveAndNotify_thenVerifyExpectedArgumentForNot 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.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 void givenTtlFromConfigIsZeroAndUseServiceTsIsTrue_whenOnMsg_thenSaveTime 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 void givenSkipLatestPersistenceIsTrueAndTtlFromConfig_whenOnMsg_thenSaveT 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 void givenTtlFromConfigAndTtlFromMd_whenOnMsg_thenVerifyTtl(String ttlFro 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 12/40] 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.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 void save(TimeseriesSaveRequest request) { } } - @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 void deleteTimeseriesAndNotify(TenantId tenantId, EntityId entityId, List 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 void onFailure(Throwable t) { }; } - 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 Builder callback(FutureCallback 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 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 Builder callback(FutureCallback 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 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.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 @@ private ListenableFuture updateMsgAndDb(TbContext ctx, TbMsg msg, Optiona 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 @@ private ListenableFuture saveAttribute(TbContext ctx, TbMsg msg, double re 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.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.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 void test_sqrt_5_to_attribute_and_metadata() { ); 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 void test_sqrt_5_to_timeseries_and_data() { ); 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 void test_sqrt_5_to_timeseries_and_metadata_and_data() { ); 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 13/40] 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.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 @@ private DeferredResult deleteAttributes(EntityId entityIdSrc, At 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 @@ private DeferredResult saveAttributes(TenantId srcTenantId, Enti } 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 @@ private DeferredResult saveTelemetry(TenantId curTenantId, Entit 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 @@ private void processEntityUsageStats(TenantId tenantId, EntityId ownerId, 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 @@ private void saveNewCounts(BaseApiUsageState state, List keys .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.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 ListenableFuture reClaimDevice(TenantId tenantId, Device d 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 @@ private ListenableFuture removeClaimingSavedData(Cache cache, ClaimDataInf 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 @@ private void onEdgeDisconnect(Edge edge, UUID sessionId) { 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 @@ private void save(TenantId tenantId, EdgeId edgeId, String key, long value) { 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 @@ private ListenableFuture processAttributesUpdate(TenantId tenantId, 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.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 @@ private ListenableFuture> copyAttributesFromEntityToEntityView(Entity (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 @@ private ListenableFuture> copyLatestFromEntityToEntityView(TenantId t }, 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 @@ private ListenableFuture deleteAttributesFromEntityView(EntityView entityV 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 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 @@ private void send(TenantId tenantId, DeviceId deviceId, OtaPackageId firmwareId, 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 @@ private void update(Device device, OtaPackageInfo otaPackage, long ts) { 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 @@ private void updateAttributes(Device device, OtaPackageInfo otaPackage, long ts, remove(device, otaPackageType, attrToRemove); - telemetryService.save(AttributesSaveRequest.builder() + telemetryService.saveAttributes(AttributesSaveRequest.builder() .tenantId(tenantId) .entityId(deviceId) .scope(AttributeScope.SHARED_SCOPE) @@ -371,8 +372,12 @@ private void remove(Device device, OtaPackageType otaPackageType) { } 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 void onSuccess(@Nullable Void tmp) { 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 @@ private void save(DeviceId deviceId, String key, boolean value) { private void save(DeviceId deviceId, KvEntry kvEntry, long ts) { if (persistToTelemetry) { - tsSubService.saveInternal(TimeseriesSaveRequest.builder() + tsSubService.saveTimeseriesInternal(TimeseriesSaveRequest.builder() .tenantId(TenantId.SYS_TENANT_ID) .entityId(deviceId) .entry(new BasicTsKvEntry(ts, kvEntry)) @@ -885,7 +885,7 @@ private void save(DeviceId deviceId, KvEntry kvEntry, long ts) { .callback(new TelemetrySaveCallback<>(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 void reportQueueStats(long ts, TbRuleEngineConsumerStats ruleEngineStats) 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 void reportQueueStats(long ts, TbRuleEngineConsumerStats ruleEngineStats) 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 @@ private void saveTelemetry(SecurityUser user, E entity, Map.Entry { 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 @@ private void saveAttributes(SecurityUser user, E entity, Map.Entry 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 @@ private void importAttributes(User user, Map> }) .collect(Collectors.toList()); // fixme: attributes are saved outside the transaction - tsSubService.save(AttributesSaveRequest.builder() + tsSubService.saveAttributes(AttributesSaveRequest.builder() .tenantId(user.getTenantId()) .entityId(entity.getId()) .scope(scope) 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 b59c723996..42d468575a 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 @@ -201,7 +201,7 @@ private void saveCurrentMonolithSystemInfo() { private void doSave(List 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.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 void shutdownExecutor() { } @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 ListenableFuture saveInternal(TimeseriesSaveRequest request) { 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 void deleteLatest(TenantId tenantId, EntityId entityId, List keys @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 void onFailure(Throwable t) { @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 void onSuccess(@Nullable List result) { } } 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 @@ private void onTimeSeriesDelete(TenantId tenantId, EntityId entityId, List 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 @@ 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 @@ */ 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 void testEntityCountCmd_filterTypeSingularCompatibilityTest() throws Exce 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 @@ private void sendAttributes(Device device, TbAttributeSubscriptionScope scope, L 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.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 void givenDeviceBelongsToMyPartition_whenOnDeviceConnect_thenReportsConne 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 void givenDeviceBelongsToMyPartition_whenOnDeviceDisconnect_thenReportsDi 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 void givenDeviceBelongsToMyPartition_whenOnDeviceInactivity_thenReportsIn 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 void givenInactivityTimeoutReached_whenUpdateInactivityStateIfExpired_the 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 void increaseInactivityForActiveDeviceTest() throws Exception { 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 void increaseSmallInactivityForInactiveDeviceTest() throws Exception { 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 void decreaseInactivityForInactiveDeviceTest() throws Exception { 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 void givenTestParameters_whenUpdateActivityState_thenShouldBeInTheExpecte // 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 void givenTestParameters_whenUpdateActivityState_thenShouldBeInTheExpecte 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 void givenTestParameters_whenUpdateActivityState_thenShouldBeInTheExpecte } 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 void givenTestParameters_whenOnDeviceInactivityTimeout_thenShouldBeInTheE 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 void givenTestParameters_whenUpdateInactivityStateIfExpired_thenShouldBeI 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 void givenTestParameters_whenUpdateInactivityStateIfExpired_thenShouldBeI 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 void givenDeviceAdded_whenOnQueueMsg_thenShouldCacheAndSaveActivityToFals // 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 void givenDeviceActivityEventHappenedAfterAdded_whenOnDeviceActivity_then // 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 void givenDeviceActivityEventHappenedBeforeAdded_whenOnQueueMsg_thenShoul // 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 ListenableFuture save(TenantId tenantId, EntityId entityId, Attribu 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 ListenableFuture> save(TenantId tenantId, EntityId entityId, A 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 ListenableFuture save(TenantId tenantId, EntityId entityId, Attribu 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 @@ private void put(EntityId entityId, AttributeScope scope, AttributeKvEntry attri 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 @@ */ 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 static class Builder { private long ttl; private FutureCallback callback; private boolean saveLatest = true; + private boolean onlyLatest; Builder() {} @@ -95,6 +97,12 @@ public Builder saveLatest(boolean saveLatest) { 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 void onFailure(Throwable t) { } 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 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 void onMsg(TbContext ctx, TbMsg msg) { 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 @@ private ListenableFuture updateMsgAndDb(TbContext ctx, TbMsg msg, Optiona 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 @@ private ListenableFuture saveAttribute(TbContext ctx, TbMsg msg, double re 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 @@ void saveAttr(List attributes, TbContext ctx, TbMsg msg, Attri 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 void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, Interrupt 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 void onMsg(TbContext ctx, TbMsg msg) { 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.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 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 void givenExistingClientAttributes_whenOnMsg_thenCopyAttributesToView() { 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 void givenExistingClientAttributes_whenOnMsg_thenCopyAttributesToView() { 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 @@ ATTRIBUTES_DELETED, DEVICE_ID, new TbMsgMetaData(Map.of(DataConstants.SCOPE, Att 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 @@ TbMsgType.POST_ATTRIBUTES_REQUEST, DEVICE_ID, new TbMsgMetaData(Map.of(DataConst 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 void test_sqrt_5_to_attribute_and_metadata() { 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 void test_sqrt_5_to_timeseries_and_data() { 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 void test_sqrt_5_to_timeseries_and_metadata_and_data() { 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.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 @@ void givenNotifyDeviceMdValue_whenSaveAndNotify_thenVerifyExpectedArgumentForNot 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.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 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 @@ void setUp() throws TbNodeException { 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 @@ void onMsg_thenVerifyOutput(boolean sendAttributesDeletedNotification, boolean n } 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 void givenTtlFromConfigIsZeroAndUseServiceTsIsTrue_whenOnMsg_thenSaveTime 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 void givenSkipLatestPersistenceIsTrueAndTtlFromConfig_whenOnMsg_thenSaveT 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 void givenTtlFromConfigAndTtlFromMd_whenOnMsg_thenVerifyTtl(String ttlFro 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 14/40] 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.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 @@ private DeferredResult deleteTimeseries(EntityId entityIdStr, St 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.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.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 void onFailure(Throwable t) { 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.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.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.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.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 void deleteAttributesInternal(AttributesDeleteRequest request) { } @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 @@ private void onTimeSeriesDelete(TenantId tenantId, EntityId entityId, List 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 ListenableFuture> removeLatest(TenantId te } @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 15/40] 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 void input(TbMsg msg, RuleChainId ruleChainId) { } 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 TbMsg newMsg(String queueName, String type, EntityId originator, Customer .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 TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, Custo .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 @@ private TbMsg entityActionM .queueName(defaultQueueName) .type(action) .originator(id) - .metaData(msgMetaData.copy()) + .copyMetaData(msgMetaData) .data(msgData) .ruleChainId(defaultRuleChainId) - .ruleNodeId(null) .build(); } @@ -558,10 +557,9 @@ private TbMsg entityActionM .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 @@ private void onTellNext(TbMsg msg, boolean useRuleNodeIdFromMsg) { targetCtx = firstNode; msg = msg.copy() .ruleChainId(entityId) - .ruleNodeId(null) + .resetRuleNodeId() .build(); } else { targetCtx = nodeActors.get(targetId); @@ -356,7 +356,7 @@ private void putToQueue(TopicPartitionInfo tpi, TbMsg msg, TbQueueCallback callb putToQueue(tpi, msg.copy() .id(UUID.randomUUID()) .ruleChainId(new RuleChainId(target.getId())) - .ruleNodeId(null) + .resetRuleNodeId() .build(), callbackWrapper); break; } 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 98269d38ac..e8c53ff5a3 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java @@ -242,7 +242,7 @@ public void deleteRpc( TbMsg msg = TbMsg.newMsg() .type(TbMsgType.RPC_DELETED) .originator(rpc.getDeviceId()) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .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 73e7966666..d8d1a83998 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -386,8 +386,7 @@ public JsonNode testScript( } TbMsg inMsg = TbMsg.newMsg() .type(msgType) - .originator(null) - .metaData(new TbMsgMetaData(metadata).copy()) + .copyMetaData(new TbMsgMetaData(metadata)) .dataType(TbMsgDataType.JSON) .data(data) .build(); 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 6fe0d5fd52..f1a272ac14 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleEngineController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleEngineController.java @@ -174,7 +174,7 @@ public void onSuccess(@Nullable DeferredResult result) { .type(TbMsgType.REST_API_REQUEST) .originator(entityId) .customerId(currentUser.getCustomerId()) - .metaData(new TbMsgMetaData(metaData).copy()) + .copyMetaData(new TbMsgMetaData(metaData)) .data(requestBody) .build(); ruleEngineCallService.processRestApiCallToRuleEngine(currentUser.getTenantId(), requestId, msg, queueName != null, 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 8089b32f3c..868adf645a 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 @@ -176,7 +176,7 @@ public void pushEntityActionToRuleEngine(EntityId entityId, HasName entity, Tena .type(msgType.get()) .originator(entityId) .customerId(customerId) - .metaData(metaData.copy()) + .copyMetaData(metaData) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(entityNode)) .build(); 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 4265747ee8..0fddcff793 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 @@ -257,7 +257,7 @@ private void pushProvisionEventToRuleEngine(ProvisionRequest request, Device dev .type(type) .originator(device.getId()) .customerId(device.getCustomerId()) - .metaData(createTbMsgMetaData(device).copy()) + .copyMetaData(createTbMsgMetaData(device)) .data(JacksonUtil.toString(entityNode)) .build(); sendToRuleEngine(device.getTenantId(), msg, null); @@ -273,7 +273,7 @@ private void pushDeviceCreatedEventToRuleEngine(Device device) { .type(TbMsgType.ENTITY_CREATED) .originator(device.getId()) .customerId(device.getCustomerId()) - .metaData(createTbMsgMetaData(device).copy()) + .copyMetaData(createTbMsgMetaData(device)) .data(JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode)) .build(); sendToRuleEngine(device.getTenantId(), msg, null); 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 ae377ac657..45f84d236d 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 @@ -578,7 +578,7 @@ private void pushRuleEngineMessage(TenantId tenantId, Edge edge, long ts, TbMsgT TbMsg tbMsg = TbMsg.newMsg() .type(msgType) .originator(edgeId) - .metaData(md.copy()) + .copyMetaData(md) .dataType(TbMsgDataType.JSON) .data(data) .build(); 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 c7526b574e..eaf3c1a95a 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 @@ -347,7 +347,7 @@ protected void pushEntityEventToRuleEngine(TenantId tenantId, EntityId entityId, .type(msgType) .originator(entityId) .customerId(customerId) - .metaData(metaData.copy()) + .copyMetaData(metaData) .dataType(TbMsgDataType.JSON) .data(msgData) .build(); 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 a68cb101a0..c4a57e4a74 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 @@ -193,7 +193,7 @@ private ListenableFuture processDeviceRpcRequestFromEdge(TenantId tenantId TbMsg tbMsg = TbMsg.newMsg() .type(TbMsgType.TO_SERVER_RPC_REQUEST) .originator(deviceId) - .metaData(metaData.copy()) + .copyMetaData(metaData) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(data)) .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 a524ccca21..bd32d18c99 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 @@ -213,10 +213,9 @@ private ListenableFuture processPostTelemetry(TenantId tenantId, CustomerI .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(entityId) .customerId(customerId) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(gson.toJson(json)) .ruleChainId(defaultQueueAndRuleChain.getValue()) - .ruleNodeId(null) .build(); edgeCtx.getClusterService().pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { @Override @@ -266,10 +265,9 @@ private ListenableFuture processPostAttributes(TenantId tenantId, Customer .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) .customerId(customerId) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(gson.toJson(json)) .ruleChainId(defaultQueueAndRuleChain.getValue()) - .ruleNodeId(null) .build(); edgeCtx.getClusterService().pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { @Override @@ -304,10 +302,9 @@ public void onSuccess(@Nullable Void tmp) { .type(TbMsgType.ATTRIBUTES_UPDATED) .originator(entityId) .customerId(customerId) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(gson.toJson(json)) .ruleChainId(defaultQueueAndRuleChain.getValue()) - .ruleNodeId(null) .build(); edgeCtx.getClusterService().pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { @Override 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 288afcb081..80368c085f 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 @@ -254,7 +254,7 @@ private void pushAssignedFromNotification(Tenant currentTenant, TenantId newTena .type(TbMsgType.ENTITY_ASSIGNED_FROM_TENANT) .originator(assignedDevice.getId()) .customerId(assignedDevice.getCustomerId()) - .metaData(getMetaDataForAssignedFrom(currentTenant).copy()) + .copyMetaData(getMetaDataForAssignedFrom(currentTenant)) .dataType(TbMsgDataType.JSON) .data(data) .build(); 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 49c0b6cc30..5ee741fd1f 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 @@ -187,7 +187,7 @@ private void sendRpcRequestToRuleEngine(ToDeviceRpcRequest msg, SecurityUser cur .type(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) .originator(msg.getDeviceId()) .customerId(Optional.ofNullable(currentUser).map(User::getCustomerId).orElse(null)) - .metaData(metaData.copy()) + .copyMetaData(metaData) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(entityNode)) .build(); 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 5936238e39..8b4941323f 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 @@ -66,7 +66,7 @@ private void pushRpcMsgToRuleEngine(TenantId tenantId, Rpc rpc) { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.valueOf("RPC_" + rpc.getStatus().name())) .originator(rpc.getDeviceId()) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .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 6285778efc..97cbbc38af 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 @@ -861,7 +861,7 @@ private void pushRuleEngineMessage(DeviceStateData stateData, TbMsgType msgType) .type(msgType) .originator(stateData.getDeviceId()) .customerId(stateData.getCustomerId()) - .metaData(md.copy()) + .copyMetaData(md) .dataType(TbMsgDataType.JSON) .data(data) .build(); diff --git a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java index c27a38df15..09fc1be1c8 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java @@ -366,7 +366,7 @@ private TransportApiResponseMsg handle(GetOrCreateDeviceFromGatewayRequestMsg re .type(TbMsgType.ENTITY_CREATED) .originator(deviceId) .customerId(customerId) - .metaData(metaData.copy()) + .copyMetaData(metaData) .dataType(TbMsgDataType.JSON) .data(JacksonUtil.toString(entityNode)) .build(); diff --git a/application/src/test/java/org/thingsboard/server/actors/rule/DefaultTbContextTest.java b/application/src/test/java/org/thingsboard/server/actors/rule/DefaultTbContextTest.java index f85f990f9e..29d96347ea 100644 --- a/application/src/test/java/org/thingsboard/server/actors/rule/DefaultTbContextTest.java +++ b/application/src/test/java/org/thingsboard/server/actors/rule/DefaultTbContextTest.java @@ -931,7 +931,7 @@ private TbMsg getTbMsgWithCallback(TbMsgCallback callback) { return TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(TENANT_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_STRING) .callback(callback) .build(); @@ -942,7 +942,7 @@ private TbMsg getTbMsgWithQueueName() { .queueName(DataConstants.MAIN_QUEUE_NAME) .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(TENANT_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_STRING) .build(); } @@ -951,7 +951,7 @@ private TbMsg getTbMsg() { return TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(TENANT_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_STRING) .build(); } diff --git a/application/src/test/java/org/thingsboard/server/controller/RuleEngineControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/RuleEngineControllerTest.java index c9e08c02c2..91dbb9714d 100644 --- a/application/src/test/java/org/thingsboard/server/controller/RuleEngineControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/RuleEngineControllerTest.java @@ -65,7 +65,7 @@ public void testHandleRuleEngineRequestWithMsgOriginatorUser() throws Exception TbMsg responseMsg = TbMsg.newMsg() .type(TbMsgType.REST_API_REQUEST) .originator(currentUserId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(RESPONSE_BODY) .build(); mockRestApiCallToRuleEngine(responseMsg); @@ -94,7 +94,7 @@ public void testHandleRuleEngineRequestWithMsgOriginatorDevice() throws Exceptio TbMsg responseMsg = TbMsg.newMsg() .type(TbMsgType.REST_API_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(RESPONSE_BODY) .build(); mockRestApiCallToRuleEngine(responseMsg); @@ -123,7 +123,7 @@ public void testHandleRuleEngineRequestWithMsgOriginatorDeviceAndSpecifiedTimeou TbMsg responseMsg = TbMsg.newMsg() .type(TbMsgType.REST_API_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(RESPONSE_BODY) .build(); mockRestApiCallToRuleEngine(responseMsg); @@ -175,7 +175,7 @@ public void testHandleRuleEngineRequestWithMsgOriginatorDeviceAndSpecifiedQueue( .queueName(DataConstants.HP_QUEUE_NAME) .type(TbMsgType.REST_API_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(RESPONSE_BODY) .build(); mockRestApiCallToRuleEngine(responseMsg); @@ -220,7 +220,7 @@ public void testHandleRuleEngineRequestWithAuthorityCustomerUser() throws Except .type(TbMsgType.REST_API_REQUEST) .originator(deviceId) .customerId(customerId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(RESPONSE_BODY) .build(); mockRestApiCallToRuleEngine(responseMsg); 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 b09d2752cf..e78cd67ba3 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java @@ -746,7 +746,7 @@ private TbMsg publishTbMsg(TenantId tenantId, TopicPartitionInfo tpi) { TbMsg tbMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(tenantId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data("{\"test\":1}") .build(); TransportProtos.ToRuleEngineMsg msg = TransportProtos.ToRuleEngineMsg.newBuilder() 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 9926fb2c01..1ec1ed4fb6 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 @@ -187,7 +187,7 @@ public void testRuleChainWithTwoRules() throws Exception { TbMsg tbMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(device.getId()) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .callback(tbMsgCallback) .build(); @@ -318,7 +318,7 @@ public void testTwoRuleChainsWithTwoRules() throws Exception { TbMsg tbMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(device.getId()) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .callback(tbMsgCallback) .build(); 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 7618a6a373..1c67689814 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 @@ -145,7 +145,7 @@ public void testRuleChainWithOneRule() throws Exception { TbMsg tbMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(device.getId()) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .callback(tbMsgCallback) .build(); 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 81a1120d4e..4dae5a6427 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 @@ -295,7 +295,7 @@ public void testPushMsgToRuleEngineWithTenantIdIsNullUuidAndEntityIsTenantUseQue .queueName(DataConstants.HP_QUEUE_NAME) .type(TbMsgType.REST_API_REQUEST) .originator(tenantId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); @@ -314,7 +314,7 @@ public void testPushMsgToRuleEngineWithTenantIdIsNullUuidAndEntityIsDevice() { TbMsg requestMsg = TbMsg.newMsg() .type(TbMsgType.REST_API_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); TbQueueCallback callback = mock(TbQueueCallback.class); @@ -336,7 +336,7 @@ public void testPushMsgToRuleEngineWithTenantIdIsNotNullUuidUseQueueFromMsgIsTru .queueName(DataConstants.HP_QUEUE_NAME) .type(TbMsgType.REST_API_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); @@ -361,7 +361,7 @@ public void testPushMsgToRuleEngineUseQueueFromMsgIsFalse() { TbMsg requestMsg = TbMsg.newMsg() .type(TbMsgType.REST_API_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); 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 9a6db83f83..0b6e70b84c 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 @@ -785,7 +785,7 @@ public void setUpTestMsg() { testMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(new DeviceId(UUID.randomUUID())) - .metaData(new TbMsgMetaData().copy()) + .copyMetaData(new TbMsgMetaData()) .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 9f849dacd7..be079017c3 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 @@ -49,7 +49,7 @@ public void givenTbMsg_whenSendRestApiCallReply_thenPushNotificationToCore() { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.REST_API_REQUEST) .originator(deviceId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); var restApiCallResponseMsgProto = TransportProtos.RestApiCallResponseMsgProto.newBuilder() 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 3196861020..a4112ef8d6 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 @@ -89,7 +89,7 @@ void givenRequest_whenProcessRestApiCallToRuleEngine_thenPushMsgToRuleEngineAndC .queueName(DataConstants.MAIN_QUEUE_NAME) .type(TbMsgType.REST_API_REQUEST) .originator(TENANT_ID) - .metaData(new TbMsgMetaData(metaData).copy()) + .copyMetaData(new TbMsgMetaData(metaData)) .data("{\"key\":\"value\"}") .build(); @@ -123,7 +123,7 @@ void givenResponse_whenOnQueue_thenAcceptTbMsgResponse() { .queueName(DataConstants.MAIN_QUEUE_NAME) .type(TbMsgType.REST_API_REQUEST) .originator(TENANT_ID) - .metaData(new TbMsgMetaData(metaData).copy()) + .copyMetaData(new TbMsgMetaData(metaData)) .data("{\"key\":\"value\"}") .build(); 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 cee0aa6428..079bf8e1e2 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 @@ -134,7 +134,7 @@ void saveLatestTsForAssetAndDevice(List devices, Asset asset, int idx) t TbMsg tbMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(device.getId()) - .metaData(getTbMsgMetadata(device.getName(), ts.get(idx)).copy()) + .copyMetaData(getTbMsgMetadata(device.getName(), ts.get(idx))) .dataType(TbMsgDataType.JSON) .data(getTbMsgData(msgValue.get(idx))) .build(); 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 797ef3ce4f..64e05770fe 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 @@ -88,7 +88,7 @@ public TbMsgBuilder copy() { public TbMsg transform(String queueName) { return transform() .queueName(queueName) - .ruleNodeId(null) + .resetRuleNodeId() .build(); } @@ -415,6 +415,11 @@ public TbMsgBuilder metaData(TbMsgMetaData metaData) { return this; } + public TbMsgBuilder copyMetaData(TbMsgMetaData metaData) { + this.metaData = metaData.copy(); + return this; + } + public TbMsgBuilder dataType(TbMsgDataType dataType) { this.dataType = dataType; return this; @@ -435,6 +440,10 @@ public TbMsgBuilder ruleNodeId(RuleNodeId ruleNodeId) { return this; } + public TbMsgBuilder resetRuleNodeId() { + return ruleNodeId(null); + } + public TbMsgBuilder correlationId(UUID correlationId) { this.correlationId = correlationId; return this; 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 85e8b4cfd1..af9dca8583 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 @@ -1141,10 +1141,9 @@ private void sendToRuleEngine(TenantId tenantId, DeviceId deviceId, CustomerId c .type(tbMsgType) .originator(deviceId) .customerId(customerId) - .metaData(metaData.copy()) + .copyMetaData(metaData) .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/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 38db78d221..0aa3077add 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 @@ -47,7 +47,7 @@ public void testSimpleReplacement() { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(TenantId.SYS_TENANT_ID) - .metaData(md.copy()) + .copyMetaData(md) .data(JacksonUtil.toString(node)) .build(); String result = TbNodeUtils.processPattern(pattern, msg); @@ -66,7 +66,7 @@ public void testNoReplacement() { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(TenantId.SYS_TENANT_ID) - .metaData(md.copy()) + .copyMetaData(md) .data(JacksonUtil.toString(node)) .build(); String result = TbNodeUtils.processPattern(pattern, msg); @@ -85,7 +85,7 @@ public void testSameKeysReplacement() { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(TenantId.SYS_TENANT_ID) - .metaData(md.copy()) + .copyMetaData(md) .data(JacksonUtil.toString(node)) .build(); String result = TbNodeUtils.processPattern(pattern, msg); @@ -111,7 +111,7 @@ public void testComplexObjectReplacement() { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(TenantId.SYS_TENANT_ID) - .metaData(md.copy()) + .copyMetaData(md) .data(JacksonUtil.toString(node)) .build(); String result = TbNodeUtils.processPattern(pattern, msg); @@ -137,7 +137,7 @@ public void testArrayReplacementDoesNotWork() { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(TenantId.SYS_TENANT_ID) - .metaData(md.copy()) + .copyMetaData(md) .data(JacksonUtil.toString(node)) .build(); String result = TbNodeUtils.processPattern(pattern, msg); 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 f54f442e57..edbd9c7ca4 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 @@ -79,7 +79,7 @@ public void onMsg(TbContext ctx, TbMsg msg) { .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(ctx.getTenantId()) .customerId(msg.getCustomerId()) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(gson.toJson(telemetryJson)) .build(); ctx.enqueueForTellNext(tbMsg, TbNodeConnectionType.SUCCESS); 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 4644696886..cf4b5b4ce1 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 @@ -158,7 +158,7 @@ private void processDeduplication(TbContext ctx, EntityId deduplicationId) { .queueName(queueName) .type(config.getOutMsgType()) .originator(deduplicationId) - .metaData(getMetadata().copy()) + .copyMetaData(getMetadata()) .data(getMergedData(pack)) .build()); } else { @@ -183,7 +183,7 @@ private void processDeduplication(TbContext ctx, EntityId deduplicationId) { .type(resultMsg.getType()) .originator(resultMsg.getOriginator()) .customerId(resultMsg.getCustomerId()) - .metaData(resultMsg.getMetaData().copy()) + .copyMetaData(resultMsg.getMetaData()) .data(resultMsg.getData()) .build()); } 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 e3887db026..a8a2c4e9b5 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 @@ -70,7 +70,7 @@ public void onMsg(TbContext ctx, TbMsg msg) { .type(pendingMsg.getType()) .originator(pendingMsg.getOriginator()) .customerId(pendingMsg.getCustomerId()) - .metaData(pendingMsg.getMetaData().copy()) + .copyMetaData(pendingMsg.getMetaData()) .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 e1f0531195..c57a9a1433 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 @@ -182,7 +182,7 @@ protected void scheduleAlarmHarvesting(TbContext ctx, TbMsg msg) { .type(TbMsgType.DEVICE_PROFILE_PERIODIC_SELF_MSG) .originator(ctx.getTenantId()) .customerId(customerId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); ctx.tellSelf(periodicCheck, TimeUnit.MINUTES.toMillis(1)); @@ -212,7 +212,7 @@ protected void onProfileUpdate(DeviceProfile profile) { ctx.tellSelf(TbMsg.newMsg() .type(TbMsgType.DEVICE_PROFILE_UPDATE_SELF_MSG) .originator(ctx.getTenantId()) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(profile.getId().getId().toString()) .build(), 0L); } @@ -226,7 +226,7 @@ private void onDeviceUpdate(DeviceId deviceId, DeviceProfile deviceProfile) { ctx.tellSelf(TbMsg.newMsg() .type(TbMsgType.DEVICE_UPDATE_SELF_MSG) .originator(ctx.getTenantId()) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(JacksonUtil.toString(msgData)) .build(), 0L); } 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 b6cdb0b7d3..6a136a473b 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 @@ -309,7 +309,7 @@ private TbMsg getTbMsg(EntityId originator) { 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/action/TbClearAlarmNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbClearAlarmNodeTest.java index e8deedba09..a50c2ff5aa 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 @@ -94,7 +94,7 @@ void alarmCanBeCleared() { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(msgOriginator) - .metaData(metadata.copy()) + .copyMetaData(metadata) .data("{\"temperature\": 50}") .build(); @@ -151,7 +151,7 @@ void alarmCanBeClearedWithAlarmOriginator() { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(alarmOriginator) - .metaData(metadata.copy()) + .copyMetaData(metadata) .data("{\"temperature\": 50}") .build(); 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 b0d900c7e2..5caa8b98ee 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 @@ -109,7 +109,7 @@ public void givenExistingClientAttributes_whenOnMsg_thenCopyAttributesToView() { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(DEVICE_ID) - .metaData(new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name())).copy()) + .copyMetaData(new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name()))) .data("{\"clientAttribute1\": 100, \"clientAttribute2\": \"value2\"}") .build(); @@ -146,7 +146,7 @@ public void givenExistingServerAttributesAndMsgTypeAttributesDeleted_whenOnMsg_t TbMsg msg = TbMsg.newMsg() .type(ATTRIBUTES_DELETED) .originator(DEVICE_ID) - .metaData(new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name())).copy()) + .copyMetaData(new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name()))) .data("{\"attributes\": [\"serverAttribute1\"]}") .build(); @@ -180,7 +180,7 @@ public void givenNonMatchedSharedAttributesAndMsgTypeIsAttributesDeleted_whenOnM TbMsg msg = TbMsg.newMsg() .type(TbMsgType.ATTRIBUTES_DELETED) .originator(DEVICE_ID) - .metaData(new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SHARED_SCOPE.name())).copy()) + .copyMetaData(new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SHARED_SCOPE.name()))) .data("{\"attributes\": [\"anotherAttribute\"]}") .build(); @@ -200,7 +200,7 @@ public void givenNonMatchedAttributesAndMsgTypeIsPostAttributesRequest_whenOnMsg TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(DEVICE_ID) - .metaData(new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name())).copy()) + .copyMetaData(new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name()))) .data("{\"clientAttribute2\": \"value2\"}") .build(); @@ -235,7 +235,7 @@ public void givenAttributesValidityPeriodOutOfStartDateAndEndDate_whenOnMsg_then TbMsg msg = TbMsg.newMsg() .type(ATTRIBUTES_DELETED) .originator(DEVICE_ID) - .metaData(new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name())).copy()) + .copyMetaData(new TbMsgMetaData(Map.of(DataConstants.SCOPE, AttributeScope.SERVER_SCOPE.name()))) .data("{\"attributes\": [\"serverAttribute1\"]}") .build(); node.onMsg(ctxMock, msg); @@ -251,7 +251,7 @@ public void givenMsgTypeAndEmptyMetadata_whenOnMsg_thenVerifyFailureMsg(TbMsgTyp TbMsg msg = 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/action/TbCreateAlarmNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java index 5f35288c30..370c9f1347 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 @@ -166,7 +166,7 @@ void whenAlarmDataIsTakenFromDefaultNodeConfigAndAlarmDoesNotExist_thenNewAlarmI var incomingMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(msgOriginator) - .metaData(metadata.copy()) + .copyMetaData(metadata) .data("{\"temperature\": 50}") .build(); @@ -325,7 +325,7 @@ void whenAlarmDataIsTakenFromNodeConfigAndClearedAlarmExists_thenNewAlarmIsCreat var incomingMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(msgOriginator) - .metaData(metadata.copy()) + .copyMetaData(metadata) .data("{\"temperature\": 50, \"alarmType\": \"" + alarmType + "\"}") .build(); @@ -521,7 +521,7 @@ void whenAlarmDataIsTakenFromNodeConfigAndActiveAlarmExists_thenExistingAlarmIsU var incomingMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(msgOriginator) - .metaData(metadata.copy()) + .copyMetaData(metadata) .data("{\"temperature\": 50, \"alarmSeverity\": \"" + newAlarmSeverity.name() + "\"}") .build(); @@ -698,7 +698,7 @@ void whenAlarmDataIsTakenFromMsgAndClearedAlarmExists_thenNewAlarmIsCreated() th var incomingMsg = TbMsg.newMsg() .type(TbMsgType.ALARM) .originator(msgOriginator) - .metaData(metadata.copy()) + .copyMetaData(metadata) .data(JacksonUtil.toString(alarmFromIncomingMessage)) .build(); @@ -890,7 +890,7 @@ void whenAlarmDataIsTakenFromMsgAndActiveAlarmExists_thenExistingAlarmIsUpdated( var incomingMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(msgOriginator) - .metaData(metadata.copy()) + .copyMetaData(metadata) .data(JacksonUtil.toString(alarmFromIncomingMessage)) .build(); @@ -1076,7 +1076,7 @@ void whenOnlySeverityWasUpdated_thenShouldTakeAlarmUpdatedPath() throws Exceptio var incomingMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(msgOriginator) - .metaData(metadata.copy()) + .copyMetaData(metadata) .data(JacksonUtil.toString(alarmFromIncomingMessage)) .build(); @@ -1222,7 +1222,7 @@ void whenAlarmDetailsScriptThrowsException_thenShouldTellFailureAndNoOtherAction var incomingMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(msgOriginator) - .metaData(metadata.copy()) + .copyMetaData(metadata) .data("{\"temperature\": 50}") .build(); 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 37d7ee7872..cf4eec3072 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 @@ -491,7 +491,7 @@ void givenSupportedEntityType_whenOnMsg_thenVerifyRelationAndEntityCreatedAndOut var entityCreatedMsg = TbMsg.newMsg() .type(TbMsgType.ENTITY_CREATED) .originator(entityId) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); mockMethodCallsMap.get(entityType).accept(entity, entityCreatedMsg); @@ -686,7 +686,7 @@ private TbMsg getTbMsg(EntityId originator, TbMsgMetaData metaData) { return TbMsg.newMsg() .type(TbMsgType.NA) .originator(originator) - .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/action/TbDeleteRelationNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbDeleteRelationNodeTest.java index 9a2eeb114e..ac530bd93b 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 @@ -563,7 +563,7 @@ private TbMsg getTbMsg(EntityId originator, TbMsgMetaData metaData) { return TbMsg.newMsg() .type(TbMsgType.NA) .originator(originator) - .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/action/TbDeviceStateNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbDeviceStateNodeTest.java index 12e16ab8c6..6dff2b90b0 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 @@ -88,7 +88,7 @@ public void setup() { msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(JacksonUtil.toString(data)) .build(); } @@ -215,7 +215,7 @@ public EntityType getEntityType() { var msg = TbMsg.newMsg() .type(TbMsgType.ENTITY_CREATED) .originator(nonDeviceOriginator) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_JSON_OBJECT) .build(); @@ -250,7 +250,7 @@ public void givenMetadataDoesNotContainTs_whenOnMsg_thenMsgTsIsUsedAsEventTs() { .ts(msgTs) .type(TbMsgType.POST_TELEMETRY_REQUEST) .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/action/TbLogNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbLogNodeTest.java index cd40154e5e..70ebc8dbef 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 @@ -53,7 +53,7 @@ void givenMsg_whenToLog_thenReturnString() { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(TenantId.SYS_TENANT_ID) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(data) .build(); @@ -74,7 +74,7 @@ void givenEmptyDataMsg_whenToLog_thenReturnString() { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(TenantId.SYS_TENANT_ID) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data("") .build(); @@ -95,7 +95,7 @@ void givenNullDataMsg_whenToLog_thenReturnString() { TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(TenantId.SYS_TENANT_ID) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(null) .build(); 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 a29801a92d..9e5f744b53 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 @@ -66,7 +66,7 @@ public class TbMsgCountNodeTest { private final TbMsg tickMsg = TbMsg.newMsg() .type(TbMsgType.MSG_COUNT_SELF_MSG) .originator(RULE_NODE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_STRING) .build(); @@ -128,7 +128,7 @@ public void givenIncomingMsgs_whenOnMsg_thenSendsMsgWithMsgCount() throws TbNode var msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_STRING) .build(); if (msgWithCounterSent.get()) { @@ -155,7 +155,7 @@ public void givenIncomingMsgs_whenOnMsg_thenSendsMsgWithMsgCount() throws TbNode TbMsg expectedMsg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(TENANT_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(expectedData) .build(); assertThat(resultedMsg).usingRecursiveComparison() 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 98e32474a3..eae95e7025 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 @@ -188,7 +188,7 @@ public void givenInvalidMessageStructure_whenOnMsg_thenThrowsException() throws TbMsg msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(DEVICE_ID) - .metaData(TbMsgMetaData.EMPTY.copy()) + .copyMetaData(TbMsgMetaData.EMPTY) .data(TbMsg.EMPTY_STRING) .build(); assertThatThrownBy(() -> node.onMsg(ctxMock, msg)) @@ -214,7 +214,7 @@ public void givenDataKeyIsMissingInMsg_whenOnMsg_thenThrowsException() throws Tb 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 void givenUnsupportedData_whenOnMsg_thenThrowsException() throws TbNodeEx 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 void givenTtl_whenOnMsg_thenVerifyStatement(int ttlFromConfig, 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 void givenValidMsgStructure_whenOnMsg_thenVerifyMatchOfValuesInsertionOrd 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 @@ private TbMsg getTbMsg(EntityId originator) { 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 void givenRequest_whenOnMsg_thenTellSuccess(String data, TbMsgMetaData me 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 void givenExceptionWasThrownInsideFunctionAndTellFailureIfFuncThrowsExcIs 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 void givenExceptionWasThrownInsideFunctionAndTellFailureIfFuncThrowsExcIs 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 void givenPayloadFromResultIsNull_whenOnMsg_thenTellFailure() { 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 void givenExceptionWasThrownOnAWS_whenOnMsg_thenTellFailure() { 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 @@ void givenForceAckIsTrueAndTopicNamePattern_whenOnMsg_thenEnqueueForTellNext(Str 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 @@ void givenForceAckIsFalseAndErrorOccursDuringProcessingRequest_whenOnMsg_thenTel 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 @@ void givenQueueUrlPatternsAndQueueTypeIsFifo_whenOnMsg_thenVerifyRequest(String 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 @@ void givenMsgAttributesPatternsAndQueueTypeIsStandard_whenOnMsg_thenVerifyReques 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 @@ void givenForceAckIsTrueAndMsgResultContainsBodyAndAttributesAndNumber_whenOnMsg 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 @@ void givenForceAckIsFalseAndErrorOccursDuringProcessingRequest_whenOnMsg_thenTel 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 void givenMsgCountAndDelay_whenInit_thenVerifyInvocationOfOnMsgMethod() t 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 void givenMsgCountAndDelay_whenInit_thenVerifyInvocationOfOnMsgMethod() t 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 void givenMsgCountAndDelay_whenInit_thenVerifyInvocationOfOnMsgMethod() t 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 void givenMsgCountAndDelay_whenInit_thenVerifyInvocationOfOnMsgMethod() t 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 void ackMsgInCaseNoEdgeRelated() { 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 void testAttributeUpdateMsg_userEntity() { 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 @@ private void testEvent(TbMsgType event, TbMsgMetaData metaData, EdgeEventActionT 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 @@ private TbMsg getTbMsg(EntityId entityId) { 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 @@ private TbMsg getTbMsg(String msgData) { 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 @@ private TbMsg getTbMsg(boolean emptyData) { 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 @@ private TbMsg getTbMsg(EntityId entityId) { 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 void falseEvaluationDoNotSendMsg() throws TbNodeException { 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 void exceptionInJsThrowsException() throws TbNodeException { 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 void metadataConditionCanBeTrue() throws TbNodeException { 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 void multipleRoutesAreAllowed() throws TbNodeException { 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 @@ private TbMsg getTbMsg(EntityId entityId, TbMsgType msgType) { 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 @@ private TbMsg getTbMsg(TbMsgType msgType) { 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 @@ private TbMsg getTbMsg(EntityId entityId) { 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 @@ private TbMsg getTbMsg(EntityId entityId) { 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 void givenMsg_whenOnMsg_thenAckAndTellSuccess() throws TbNodeException { 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 void givenQueueName_whenOnMsg_thenTransfersMsgToDefinedQueue(String queue 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 void givenErrorDuringTransfer_whenOnMsg_thenTellFailure() throws TbNodeEx 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 @@ private TbMsg getMsg(EntityId entityId) { 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 void givenRuleNodeName_whenOnMsg_thenForwardMsgToTheCallerRuleChainWithRe 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 @@ public void givenForceAckIsTrueAndMessageAttributesPatterns_whenOnMsg_thenEnqueu 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 @@ public void givenForceAckIsFalse_whenOnMsg_thenTellSuccess() throws IOException, 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 @@ public void givenForceAckIsFalseAndErrorOccursOnTheGCP_whenOnMsg_thenTellFailure 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 @@ public void givenForceAckIsTrueAndErrorOccursOnTheGCP_whenOnMsg_thenEnqueueForTe 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 @@ private TbMsg getTbMsg(EntityId entityId, TbMsgMetaData metadata, double latitud 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 @@ private TbMsg getTbMsg(EntityId entityId, TbMsgMetaData metadata, double latitud return TbMsg.newMsg() .type(TbMsgType.POST_ATTRIBUTES_REQUEST) .originator(entityId) - .metaData(metadata.copy()) + .copyMetaData(metadata) .data(data) .build(); } @@ -460,7 +460,7 @@ private TbMsg getEmptyArrayTbMsg(EntityId entityId) { 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 void givenInitErrorIsNotNull_whenOnMsg_thenTellFailure() { 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 void givenForceAckAndExceptionWasThrown_whenOnMsg_thenTellFailure(boolean 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 void givenForceAckIsTrueTopicAndKeyPatternsAndAddMetadataKeyValuesAsKafka 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 void givenForceAckIsFalseAndKeyIsNullOrEmptyAndErrorOccursDuringPublishin 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 void givenForceAckIsTrueAndAddKafkaHeadersIsTrueAndToBytesCharsetIsNullAn 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 void givenForceAckIsFalseAndAddMetadataKeyValuesAsKafkaHeadersIsTrueAndTo 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 void givenMailBodyTypeTestConfig_whenOnMsg_thenVerify(MailBodyTypeTestCon 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 void testExp4j() { 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 void testExp4j() { msg = TbMsg.newMsg() .type(TbMsgType.POST_TELEMETRY_REQUEST) .originator(originator) - .metaData(metaData.copy()) + .copyMetaData(metaData) .data(msgNode.toString()) .build(); @@ -241,7 +241,7 @@ public void testSimpleTwoArgumentFunction(TbRuleNodeMathFunctionType function, d 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 void testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType function, d 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 void test_2_plus_2_body() { 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 void test_2_plus_2_meta() { 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 void test_2_plus_2_attr_and_ts() { 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 void test_sqrt_5_body() { 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 void test_sqrt_5_meta() { 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 void test_sqrt_5_to_attribute_and_metadata() { 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 void test_sqrt_5_to_timeseries_and_data() { 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 void test_sqrt_5_to_timeseries_and_metadata_and_data() { 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 void test_sqrt_5_default_value() { 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 void test_sqrt_5_default_value_failure() { 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 void testConvertMsgBodyIfRequiredFailure() { 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 void testExp4j_concurrent() { .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 void testExp4j_concurrent() { .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 void testExp4j_concurrentBySingleOriginator_processMsgAsyncException() { .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 void testExp4j_concurrentBySingleOriginator_SingleMsg_manyNodesWithDiffer .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 void givenInvalidMsgType_whenOnMsg_thenShouldTellNextOther() throws TbNod 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 void givenInvalidMsgDataType_whenOnMsg_thenShouldTellNextOther() throws T 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 void givenInputKeyIsNotPresent_whenOnMsg_thenShouldTellNextOther() throws 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 void givenDoubleValue_whenOnMsgAndCachingOff_thenShouldTellSuccess() thro 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 void givenLongStringValue_whenOnMsgAndCachingOff_thenShouldTellSuccess() 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 void givenValidStringValue_whenOnMsgAndCachingOff_thenShouldTellSuccess() 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 void givenTwoMessagesAndPeriodOnAndCachingOn_whenOnMsg_thenVerify() throw 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 void givenTwoMessagesAndPeriodOnAndCachingOn_whenOnMsg_thenVerify() throw 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 void givenLastValueIsNull_whenOnMsgAndCachingOff_thenDeltaShouldBeZero() 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 void givenNegativeDeltaAndTellFailureIfNegativeDeltaTrue_whenOnMsg_thenSh 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 void givenNegativeDeltaAndTellFailureIfNegativeDeltaFalse_whenOnMsg_thenS 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 void givenInvalidStringValue_whenOnMsg_thenException() throws TbNodeExcep 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 void givenBooleanValue_whenOnMsg_thenException() throws TbNodeException { 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 void givenJsonValue_whenOnMsg_thenException() throws TbNodeException { 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 void givenConcurrentAccess_whenOnMsg_thenGetFromDBInvokedOnce() throws Tb 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 void givenCalculateDeltaConfig_whenOnMsg_thenVerify(CalculateDeltaTestCon 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 @@ private TbMsg getTbMsg(EntityId entityId) { 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 void givenFetchLatestTimeseriesToDataAndDataIsNotJsonObject_whenOnMsg_the 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 @@ private TbMsg getTbMsg(EntityId entityId) { 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 void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException( 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 void givenDidNotFindEntity_whenOnMsg_thenShouldTellFailure() { 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 @@ private void prepareMsgAndConfig(TbMsgSource fetchTo, DataToFetch dataToFetch, E 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 void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException( 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 @@ private void prepareMsgAndConfig(TbMsgSource fetchTo, List node.onMsg(ctxMock, msg)) @@ -213,7 +213,7 @@ public void givenUseMetadataIntervalPatternsIsTrue_whenOnMsg_thenVerifyStartAndE 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 void givenUseMetadataIntervalPatternsIsFalse_whenOnMsg_thenVerifyStartAnd 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 void givenTsKeyNamesPatterns_whenOnMsg_thenVerifyTsKeyNamesInQuery() thro 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 void givenAggregation_whenOnMsg_thenVerifyAggregationStepInQuery(Aggregat 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 void givenFetchModeAndLimit_whenOnMsg_thenVerifyLimitInQuery(FetchMode fe 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 void givenFetchModeAndOrder_whenOnMsg_thenVerifyOrderInQuery(FetchMode fe 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 void givenInvalidIntervalPatterns_whenOnMsg_thenThrowsException(String st 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 void givenFetchModeAll_whenOnMsg_thenTellSuccessAndVerifyMsg() throws TbN 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 void givenFetchMode_whenOnMsg_thenTellSuccessAndVerifyMsg(String fetchMod 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 void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException( 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 @@ private void prepareMsgAndConfig(TbMsgSource fetchTo, DataToFetch dataToFetch, E 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 void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException( 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 @@ private void prepareMsgAndConfig(TbMsgSource fetchTo, List { 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 void testRandomMessageType() throws Exception { 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 void testEmptyProfile() throws Exception { 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 void testAlarmCreate() throws Exception { 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 void testAlarmCreate() throws Exception { 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 void testAlarmCreate() throws Exception { 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 void testAlarmCreate() throws Exception { 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 void testAlarmSeverityUpdate() throws Exception { 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 void testAlarmSeverityUpdate() throws Exception { 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 void testAlarmSeverityUpdate() throws Exception { 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 void testAlarmSeverityUpdate() throws Exception { 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 void testConstantKeyFilterSimple() throws Exception { 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 void testConstantKeyFilterSimple() throws Exception { 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 void testConstantKeyFilterInherited() throws Exception { 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 void testConstantKeyFilterInherited() throws Exception { 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 void testCurrentDeviceAttributeForDynamicValue() throws Exception { 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 void testCurrentDeviceAttributeForDynamicValue() throws Exception { 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 void testCurrentDeviceAttributeForDynamicDurationValue() throws Exception 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 void testCurrentDeviceAttributeForDynamicDurationValue() throws Exception 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 void testCurrentDeviceAttributeForDynamicDurationValue() throws Exception 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 void testInheritTenantAttributeForDuration() throws Exception { 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 void testInheritTenantAttributeForDuration() throws Exception { 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 void testInheritTenantAttributeForDuration() throws Exception { 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 void testCurrentDeviceAttributeForDynamicRepeatingValue() throws Exceptio 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 void testCurrentDeviceAttributeForDynamicRepeatingValue() throws Exceptio 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 void testCurrentDeviceAttributeForDynamicRepeatingValue() throws Exceptio 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 void testInheritTenantAttributeForRepeating() throws Exception { 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 void testInheritTenantAttributeForRepeating() throws Exception { 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 void testInheritTenantAttributeForRepeating() throws Exception { 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 void testCurrentDeviceAttributeForUseDefaultDurationWhenDynamicDurationVa 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 void testCurrentDeviceAttributeForUseDefaultDurationWhenDynamicDurationVa 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 void testCurrentDeviceAttributeForUseDefaultDurationWhenDynamicDurationVa 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 void testCurrentDeviceAttributeForUseDefaultRepeatingWhenDynamicDurationV 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 void testCurrentDeviceAttributeForUseDefaultRepeatingWhenDynamicDurationV 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 void testActiveAlarmScheduleFromDynamicValuesWhenDefaultScheduleIsInactiv 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 void testActiveAlarmScheduleFromDynamicValuesWhenDefaultScheduleIsInactiv 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 void testInactiveAlarmScheduleFromDynamicValuesWhenDefaultScheduleIsActiv 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 void testInactiveAlarmScheduleFromDynamicValuesWhenDefaultScheduleIsActiv 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 void testCurrentCustomersAttributeForDynamicValue() throws Exception { 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 void testCurrentCustomersAttributeForDynamicValue() throws Exception { 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 void testCurrentTenantAttributeForDynamicValue() throws Exception { 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 void testCurrentTenantAttributeForDynamicValue() throws Exception { 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 void testTenantInheritModeForDynamicValues() throws Exception { .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 void testTenantInheritModeForDynamicValues() throws Exception { 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 void testTenantInheritModeForDynamicValues() throws Exception { 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 void testCustomerInheritModeForDynamicValues() throws Exception { 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 void testCustomerInheritModeForDynamicValues() throws Exception { 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 void givenForceAckIsTrueAndExchangeNameAndRoutingKeyPatternsAndBasicPrope 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 void givenForceAckIsFalseAndExchangeNameAndRoutingKeyPatternsAndBasicProp 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 void givenForceAckAndErrorOccursDuringPublishing_whenOnMsg_thenVerifyTell 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 void testProcessMessageWithJsonInUrlVariable() throws Exception { 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 void run() { 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 void run() { 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 void givenValidRestApiRequest_whenOnMsg_thenTellSuccess(String requestIdA 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 void givenInvalidRequest_whenOnMsg_thenTellFailure(TbMsgMetaData metaData 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 void sendReplyToTransport() { 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 void sendReplyToEdgeQueue() { 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 void testOriginatorEntityTypes(EntityType entityType) { 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 void testForAvailabilityOfMetadataAndDataValues(TbMsgMetaData metaData, S 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 void givenOneway_whenOnMsg_thenVerifyRequest(String mdKeyValue, boolean e 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 void givenMsgBody_whenOnMsg_thenVerifyRequest() { 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 void givenRequestIdIsNotSet_whenOnMsg_thenVerifyRequest() { 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 void givenRequestId_whenOnMsg_thenVerifyRequest() { 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 void givenRequestUUID_whenOnMsg_thenVerifyRequest() { 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 void givenInvalidRequestUUID_whenOnMsg_thenVerifyRequest(String requestUU 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 void givenOriginServiceId_whenOnMsg_thenVerifyRequest() { 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 void givenInvalidOriginServiceId_whenOnMsg_thenVerifyRequest(String origi 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 void givenExpirationTime_whenOnMsg_thenVerifyRequest() { 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 void givenInvalidExpirationTime_whenOnMsg_thenVerifyRequest(String expira 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 void givenRetries_whenOnMsg_thenVerifyRequest() { 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 void givenInvalidRetriesValue_whenOnMsg_thenVerifyRequest(String retries) 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 void givenTbMsgType_whenOnMsg_thenVerifyRequest(TbMsgType msgType) { 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 void givenPersistent_whenOnMsg_thenVerifyRequest(String isPersisted, bool 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 void givenRpcResponseWithoutError_whenOnMsg_thenSendsRpcRequest() { 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 void givenRpcResponseWithoutError_whenOnMsg_thenSendsRpcRequest() { 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 void givenRpcResponseWithError_whenOnMsg_thenTellFailure() { 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 void givenRpcResponseWithError_whenOnMsg_thenTellFailure() { 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 void givenOriginatorIsNotDevice_whenOnMsg_thenThrowsException(EntityType 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 void givenMethodOrParamsAreNotPresent_whenOnMsg_thenThrowsException(Strin 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 @@ void givenNotifyDeviceMdValue_whenSaveAndNotify_thenVerifyExpectedArgumentForNot 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 @@ void onMsg_thenVerifyOutput(boolean sendAttributesDeletedNotification, boolean n 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 void givenMsgTypeAndEmptyMsgData_whenOnMsg_thenVerifyFailureMsg(TbMsgType 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 void givenTtlFromConfigIsZeroAndUseServiceTsIsTrue_whenOnMsg_thenSaveTime 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 void givenSkipLatestPersistenceIsTrueAndTtlFromConfig_whenOnMsg_thenSaveT 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 void givenTtlFromConfigAndTtlFromMd_whenOnMsg_thenVerifyTtl(String ttlFro 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 void givenOriginatorSourceIsCustomer_whenOnMsg_thenTellSuccess() throws T 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 void givenOriginatorSourceIsTenant_whenOnMsg_thenTellSuccess() throws TbN 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 void givenOriginatorSourceIsRelatedAndNewOriginatorIsNull_whenOnMsg_thenT 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 void givenOriginatorSourceIsAlarmOriginator_whenOnMsg_thenTellSuccess() t 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 void givenOriginatorSourceIsEntity_whenOnMsg_thenTellSuccess(String entit 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 void givenOriginatorSourceIsEntityAndEntityCouldNotFound_whenOnMsg_thenTe 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 @@ private TbMsg getTbMsg(EntityId entityId, String data) { 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 @@ private TbMsg getTbMsg(EntityId entityId, String data) { 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 @@ private TbMsg getTbMsg(EntityId entityId, String data) { 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 void init() throws TbNodeException { 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 @@ private TbMsg createMsg(DeviceId deviceId, long ts) { .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 @@ private TbMsg getTbMsg(EntityId entityId, String data) { 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 @@ private TbMsg getTbMsg(EntityId entityId, String data) { 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 void metadataCanBeUpdated() throws TbNodeException { 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 void metadataCanBeUpdated() throws TbNodeException { 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 void exceptionHandledCorrectly() throws TbNodeException { 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 16/40] 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 void init() throws TbNodeException { 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 17/40] 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 @@

{{(data.isAdd ? 'filter.add-key-filter' : (data.readonly ? 'filter.key-filte 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 @@

{{ filter.filter }}

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 18/40] 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 19/40] 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 20/40] 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 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.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 @@ private void sendResponseForAdaptorErrorOrCloseContext(ChannelHandlerContext ctx } 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 21/40] 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 22/40] 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 @@

{{ 'datakey.configuration' | translate }}

; 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 23/40] 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 @@

    {{ importTitle }}

    - + {{ 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 24/40] 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 25/40] 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 26/40] 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 527f7176c54710b395bbb332bfed3e3a35441657 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 1 Jan 2025 14:25:28 +0100 Subject: [PATCH 27/40] 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 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 @@ 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 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.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 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 @@ void givenScriptsWithSameBody_thenCompileAndCacheOnlyOnce() throws Exception { 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 @@ public void whenReleasingScript_thenCheckForScriptHashUsages() throws Exception 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 @@ public void whenReleasingScript_thenCheckForScriptHashUsages() throws Exception @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 28/40] 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 29/40] 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 30/40] 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 @@ 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 void cleanUp() { 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 @@ private void cleanUp(TenantId tenantId) { 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.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 @@ protected List findRelationsByTo(EntityId entityId) throws Excep 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 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 @@ }) 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 void testAlarmsCleanUp() throws Exception { 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 31/40] 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 @@ @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 @@ @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 @@ @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 @@ 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 CoapServer getCoapServer() throws UnknownHostException { } @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 void onError(Throwable e) { 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 List getAcceptedIssuers() { 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 @@ 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 @@ @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 TbCoapDtlsSessionInMemoryStorage(long dtlsSessionInactivityTimeout, long 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.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.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 @@ private void processRequest(CoapExchange exchange, CoapSessionMsgType type) { 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 @@ private void handlePostAttributesRequest(TbCoapClientState clientState, CoapExch 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 @@ private void handlePostTelemetryRequest(TbCoapClientState clientState, CoapExcha 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 void removedObserveRelation(ObserveRelation relation) { } } + 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 32/40] 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 33/40] 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 16d00361a2050dc6d50ce15fbe1b43be45f1080b Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 6 Jan 2025 17:14:39 +0200 Subject: [PATCH 34/40] UI: Fixed notification table templates/rules were not updated after creating a copy template/rule --- .../recipient/recipient-table-config.resolver.ts | 10 +++------- .../rule/rule-table-config.resolver.ts | 16 ++++++++-------- .../template/template-table-config.resolver.ts | 16 ++++++++-------- 3 files changed, 19 insertions(+), 23 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-table-config.resolver.ts index 2ffd51ec15..3d8764413d 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-table-config.resolver.ts @@ -55,7 +55,7 @@ export class RecipientTableConfigResolver { this.config.entityTranslations = entityTypeTranslations.get(EntityType.NOTIFICATION_TARGET); this.config.entityResources = {} as EntityTypeResource; - this.config.addEntity = () => this.editTarget(null, true); + this.config.addEntity = () => this.notificationTargetDialog(null, true); this.config.entitiesFetchFunction = pageLink => this.notificationService.getNotificationTargets(pageLink); @@ -72,11 +72,7 @@ export class RecipientTableConfigResolver { this.config.handleRowClick = ($event, target) => { $event?.stopPropagation(); - this.editTarget(target).subscribe((res) => { - if (res) { - this.config.updateData(); - } - }); + this.notificationTargetDialog(target).subscribe(res => res ? this.config.updateData() : null); return true; }; @@ -100,7 +96,7 @@ export class RecipientTableConfigResolver { return []; } - private editTarget(target: NotificationTarget, isAdd = false): Observable { + private notificationTargetDialog(target: NotificationTarget, isAdd = false): Observable { return this.dialog.open(RecipientNotificationDialogComponent, { disableClose: true, diff --git a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-table-config.resolver.ts index a8fbc3ff65..54f8c860f1 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-table-config.resolver.ts @@ -55,7 +55,7 @@ export class RuleTableConfigResolver { this.config.entityTranslations = entityTypeTranslations.get(EntityType.NOTIFICATION_RULE); this.config.entityResources = {} as EntityTypeResource; - this.config.addEntity = () => this.editRule(null, null, true); + this.config.addEntity = () => this.notificationRuleDialog(null, true); this.config.entitiesFetchFunction = pageLink => this.notificationService.getNotificationRules(pageLink); @@ -70,11 +70,7 @@ export class RuleTableConfigResolver { this.config.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC}; this.config.handleRowClick = ($event, rule) => { - this.editRule($event, rule).subscribe((res) => { - if (res) { - this.config.updateData(); - } - }); + this.editRule($event, rule); return true; }; @@ -109,12 +105,16 @@ export class RuleTableConfigResolver { name: this.translate.instant('notification.copy-rule'), icon: 'content_copy', isEnabled: () => true, - onAction: ($event, entity) => this.editRule($event, entity, false, true) + onAction: ($event, entity) => this.editRule($event, entity, true) }]; } - private editRule($event: Event, rule: NotificationRule, isAdd = false, isCopy = false): Observable { + private editRule($event: Event, rule: NotificationRule, isCopy = false): void{ $event?.stopPropagation(); + this.notificationRuleDialog(rule, false, isCopy).subscribe(res => res ? this.config.updateData() : null); + } + + private notificationRuleDialog(rule: NotificationRule, isAdd = false, isCopy = false): Observable { return this.dialog.open(RuleNotificationDialogComponent, { disableClose: true, diff --git a/ui-ngx/src/app/modules/home/pages/notification/template/template-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/notification/template/template-table-config.resolver.ts index 0b40b48eea..e1178da56f 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/template/template-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/template/template-table-config.resolver.ts @@ -53,7 +53,7 @@ export class TemplateTableConfigResolver { this.config.entityTranslations = entityTypeTranslations.get(EntityType.NOTIFICATION_TEMPLATE); this.config.entityResources = {} as EntityTypeResource; - this.config.addEntity = () => this.editTemplate(null, null, true); + this.config.addEntity = () => this.notificationTemplateDialog(null, true); this.config.entitiesFetchFunction = pageLink => this.notificationService.getNotificationTemplates(pageLink); @@ -68,11 +68,7 @@ export class TemplateTableConfigResolver { this.config.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC}; this.config.handleRowClick = ($event, template) => { - this.editTemplate($event, template).subscribe((res) => { - if (res) { - this.config.updateData(); - } - }); + this.editTemplate($event, template); return true; }; @@ -94,13 +90,17 @@ export class TemplateTableConfigResolver { name: this.translate.instant('notification.copy-template'), icon: 'content_copy', isEnabled: () => true, - onAction: ($event, entity) => this.editTemplate($event, entity, false, true) + onAction: ($event, entity) => this.editTemplate($event, entity, true) } ]; } - private editTemplate($event: Event, template: NotificationTemplate, isAdd = false, isCopy = false): Observable { + private editTemplate($event: Event, template: NotificationTemplate, isCopy = false) { $event?.stopPropagation(); + this.notificationTemplateDialog(template, false, isCopy).subscribe((res) => res ? this.config.updateData() : null); + } + + private notificationTemplateDialog(template: NotificationTemplate, isAdd = false, isCopy = false): Observable { return this.dialog.open(TemplateNotificationDialogComponent, { disableClose: true, From d2e949e9eb4a950dee82b0a9fd214650aa41fdf5 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 7 Jan 2025 11:39:28 +0200 Subject: [PATCH 35/40] 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 fbb8039c3da7177a066d1999ac7807f35ee9e52d Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 7 Jan 2025 11:51:51 +0200 Subject: [PATCH 36/40] UI: Improved recipient table config --- .../recipient/recipient-table-config.resolver.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-table-config.resolver.ts index 3d8764413d..5cb91db712 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-table-config.resolver.ts @@ -15,7 +15,6 @@ /// import { - CellActionDescriptor, DateEntityTableColumn, EntityTableColumn, EntityTableConfig @@ -66,13 +65,10 @@ export class RecipientTableConfigResolver { this.config.deleteEntity = id => this.notificationService.deleteNotificationTarget(id.id); - this.config.cellActionDescriptors = this.configureCellActions(); - this.config.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC}; this.config.handleRowClick = ($event, target) => { - $event?.stopPropagation(); - this.notificationTargetDialog(target).subscribe(res => res ? this.config.updateData() : null); + this.editTarget($event, target); return true; }; @@ -92,8 +88,9 @@ export class RecipientTableConfigResolver { return this.config; } - private configureCellActions(): Array> { - return []; + private editTarget($event: Event, target: NotificationTarget): void { + $event?.stopPropagation(); + this.notificationTargetDialog(target).subscribe(res => res ? this.config.updateData() : null); } private notificationTargetDialog(target: NotificationTarget, isAdd = false): Observable { From d961b89f31b974b800dee69a0018807dbe701ff2 Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 8 Jan 2025 16:17:14 +0200 Subject: [PATCH 37/40] 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 @@ public void parseBytes_Test() throws ExecutionException, InterruptedException { 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 @@ public void base64_Test() throws ExecutionException, InterruptedException { 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 static void register(ParserConfiguration parserConfig) throws Exception { 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 static void register(ParserConfiguration parserConfig) throws Exception { 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 static void register(ParserConfiguration parserConfig) throws Exception { 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 static double parseHexToDouble(String value, boolean bigEndian) { } 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 static byte[] base64ToBytes(String input) { 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 static int parseBytesToInt(byte[] data, int offset, int length, boolean b 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 static byte[] parseByteToBinaryArray(byte byteValue, int binLength) { 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 @@ private static String formatBinary(String binaryString) { } 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.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.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 void intToHexWithPrintUnsignedBytes_Test() { 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 void hexToBytes_Test() { } 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 void hexToBase64_Test() { 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 fdd7a82c9e40439ab74dfe1abad2aa02a445b3b7 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Thu, 9 Jan 2025 18:50:45 +0200 Subject: [PATCH 38/40] 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 1cd5ea4ee9abfc37814da19ade8350fbab0b5e65 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 10 Jan 2025 18:01:04 +0200 Subject: [PATCH 39/40] UI: Fixed not allow edit release note in mobile application --- .../pages/mobile/applications/mobile-app-dialog.component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-dialog.component.ts index 193fc4bbb5..6a12ec05aa 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-dialog.component.ts @@ -23,7 +23,7 @@ import { Router } from '@angular/router'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { FormGroupDirective, NgForm, UntypedFormControl } from '@angular/forms'; import { MobileApp } from '@shared/models/mobile-app.models'; -import { MobileAppComponent } from '@home/pages/mobile/applications/mobile-app.component'; +import type { MobileAppComponent } from '@home/pages/mobile/applications/mobile-app.component'; import { PlatformType } from '@shared/models/oauth2.models'; import { MobileAppService } from '@core/http/mobile-app.service'; @@ -57,6 +57,7 @@ export class MobileAppDialogComponent extends DialogComponent Date: Fri, 10 Jan 2025 19:07:03 +0200 Subject: [PATCH 40/40] 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();