diff --git a/application/pom.xml b/application/pom.xml index 6d1e009d00..33b21b3243 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.4.0-SNAPSHOT + 3.4.1-SNAPSHOT thingsboard application diff --git a/application/src/main/conf/logback.xml b/application/src/main/conf/logback.xml index 6d2c95ee84..284af71953 100644 --- a/application/src/main/conf/logback.xml +++ b/application/src/main/conf/logback.xml @@ -37,7 +37,7 @@ - + diff --git a/application/src/main/data/upgrade/3.3.4/schema_update.sql b/application/src/main/data/upgrade/3.3.4/schema_update.sql index 69df2afdc9..c86a4fe1f4 100644 --- a/application/src/main/data/upgrade/3.3.4/schema_update.sql +++ b/application/src/main/data/upgrade/3.3.4/schema_update.sql @@ -33,16 +33,7 @@ ALTER TABLE widgets_bundle ALTER TABLE entity_view ADD COLUMN IF NOT EXISTS external_id UUID; -CREATE INDEX IF NOT EXISTS idx_device_external_id ON device(tenant_id, external_id); -CREATE INDEX IF NOT EXISTS idx_device_profile_external_id ON device_profile(tenant_id, external_id); -CREATE INDEX IF NOT EXISTS idx_asset_external_id ON asset(tenant_id, external_id); -CREATE INDEX IF NOT EXISTS idx_rule_chain_external_id ON rule_chain(tenant_id, external_id); CREATE INDEX IF NOT EXISTS idx_rule_node_external_id ON rule_node(rule_chain_id, external_id); -CREATE INDEX IF NOT EXISTS idx_dashboard_external_id ON dashboard(tenant_id, external_id); -CREATE INDEX IF NOT EXISTS idx_customer_external_id ON customer(tenant_id, external_id); -CREATE INDEX IF NOT EXISTS idx_widgets_bundle_external_id ON widgets_bundle(tenant_id, external_id); -CREATE INDEX IF NOT EXISTS idx_entity_view_external_id ON entity_view(tenant_id, external_id); - CREATE INDEX IF NOT EXISTS idx_rule_node_type ON rule_node(type); ALTER TABLE admin_settings @@ -70,3 +61,80 @@ CREATE TABLE IF NOT EXISTS user_auth_settings ( two_fa_settings varchar ); +CREATE INDEX IF NOT EXISTS idx_api_usage_state_entity_id ON api_usage_state(entity_id); + +ALTER TABLE tenant_profile DROP COLUMN IF EXISTS isolated_tb_core; + +DO +$$ + BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'device_external_id_unq_key') THEN + ALTER TABLE device ADD CONSTRAINT device_external_id_unq_key UNIQUE (tenant_id, external_id); + END IF; + END; +$$; + +DO +$$ + BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'device_profile_external_id_unq_key') THEN + ALTER TABLE device_profile ADD CONSTRAINT device_profile_external_id_unq_key UNIQUE (tenant_id, external_id); + END IF; + END; +$$; + +DO +$$ + BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'asset_external_id_unq_key') THEN + ALTER TABLE asset ADD CONSTRAINT asset_external_id_unq_key UNIQUE (tenant_id, external_id); + END IF; + END; +$$; + +DO +$$ + BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'rule_chain_external_id_unq_key') THEN + ALTER TABLE rule_chain ADD CONSTRAINT rule_chain_external_id_unq_key UNIQUE (tenant_id, external_id); + END IF; + END; +$$; + + +DO +$$ + BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'dashboard_external_id_unq_key') THEN + ALTER TABLE dashboard ADD CONSTRAINT dashboard_external_id_unq_key UNIQUE (tenant_id, external_id); + END IF; + END; +$$; + +DO +$$ + BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'customer_external_id_unq_key') THEN + ALTER TABLE customer ADD CONSTRAINT customer_external_id_unq_key UNIQUE (tenant_id, external_id); + END IF; + END; +$$; + +DO +$$ + BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'widgets_bundle_external_id_unq_key') THEN + ALTER TABLE widgets_bundle ADD CONSTRAINT widgets_bundle_external_id_unq_key UNIQUE (tenant_id, external_id); + END IF; + END; +$$; + +DO +$$ + BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'entity_view_external_id_unq_key') THEN + ALTER TABLE entity_view ADD CONSTRAINT entity_view_external_id_unq_key UNIQUE (tenant_id, external_id); + END IF; + END; +$$; + diff --git a/application/src/main/data/upgrade/3.3.4/schema_update_device_profile.sql b/application/src/main/data/upgrade/3.3.4/schema_update_device_profile.sql deleted file mode 100644 index e0b0cc7f1a..0000000000 --- a/application/src/main/data/upgrade/3.3.4/schema_update_device_profile.sql +++ /dev/null @@ -1,49 +0,0 @@ --- --- Copyright © 2016-2022 The Thingsboard Authors --- --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at --- --- http://www.apache.org/licenses/LICENSE-2.0 --- --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. --- - -ALTER TABLE device_profile - ADD COLUMN IF NOT EXISTS default_queue_id uuid; - -DO -$$ - BEGIN - IF EXISTS - (SELECT column_name - FROM information_schema.columns - WHERE table_name = 'device_profile' - AND column_name = 'default_queue_name' - ) - THEN - UPDATE device_profile - SET default_queue_id = q.id - FROM queue as q - WHERE default_queue_name = q.name; - END IF; - END -$$; - -DO -$$ - BEGIN - IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'fk_default_queue_device_profile') THEN - ALTER TABLE device_profile - ADD CONSTRAINT fk_default_queue_device_profile FOREIGN KEY (default_queue_id) REFERENCES queue (id); - END IF; - END; -$$; - -ALTER TABLE device_profile - DROP COLUMN IF EXISTS default_queue_name; diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java index 7e689e58fa..a38de7b839 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -508,13 +508,8 @@ public class ActorSystemContext { return partitionService.resolve(serviceType, tenantId, entityId); } - public TopicPartitionInfo resolve(ServiceType serviceType, QueueId queueId, TenantId tenantId, EntityId entityId) { - return partitionService.resolve(serviceType, queueId, tenantId, entityId); - } - - @Deprecated public TopicPartitionInfo resolve(ServiceType serviceType, String queueName, TenantId tenantId, EntityId entityId) { - return partitionService.resolve(serviceType, tenantId, entityId, queueName); + return partitionService.resolve(serviceType, queueName, tenantId, entityId); } public String getServiceId() { 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 3538800d38..d5460689b8 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 @@ -162,18 +162,11 @@ class DefaultTbContext implements TbContext { } @Override - @Deprecated public void enqueue(TbMsg tbMsg, String queueName, Runnable onSuccess, Consumer onFailure) { TopicPartitionInfo tpi = resolvePartition(tbMsg, queueName); enqueue(tpi, tbMsg, onFailure, onSuccess); } - @Override - public void enqueue(TbMsg tbMsg, QueueId queueId, Runnable onSuccess, Consumer onFailure) { - TopicPartitionInfo tpi = resolvePartition(tbMsg, queueId); - enqueue(tpi, tbMsg, onFailure, onSuccess); - } - private void enqueue(TopicPartitionInfo tpi, TbMsg tbMsg, Consumer onFailure, Runnable onSuccess) { if (!tbMsg.isValid()) { log.trace("[{}] Skip invalid message: {}", getTenantId(), tbMsg); @@ -223,35 +216,30 @@ class DefaultTbContext implements TbContext { } @Override - public void enqueueForTellNext(TbMsg tbMsg, QueueId queueId, String relationType, Runnable onSuccess, Consumer onFailure) { - TopicPartitionInfo tpi = resolvePartition(tbMsg, queueId); - enqueueForTellNext(tpi, queueId, tbMsg, Collections.singleton(relationType), null, onSuccess, onFailure); + public void enqueueForTellNext(TbMsg tbMsg, String queueName, String relationType, Runnable onSuccess, Consumer onFailure) { + TopicPartitionInfo tpi = resolvePartition(tbMsg, queueName); + enqueueForTellNext(tpi, queueName, tbMsg, Collections.singleton(relationType), null, onSuccess, onFailure); } @Override - public void enqueueForTellNext(TbMsg tbMsg, QueueId queueId, Set relationTypes, Runnable onSuccess, Consumer onFailure) { - TopicPartitionInfo tpi = resolvePartition(tbMsg, queueId); - enqueueForTellNext(tpi, queueId, tbMsg, relationTypes, null, onSuccess, onFailure); - } - - private TopicPartitionInfo resolvePartition(TbMsg tbMsg, QueueId queueId) { - return mainCtx.resolve(ServiceType.TB_RULE_ENGINE, queueId, getTenantId(), tbMsg.getOriginator()); + public void enqueueForTellNext(TbMsg tbMsg, String queueName, Set relationTypes, Runnable onSuccess, Consumer onFailure) { + TopicPartitionInfo tpi = resolvePartition(tbMsg, queueName); + enqueueForTellNext(tpi, queueName, tbMsg, relationTypes, null, onSuccess, onFailure); } - @Deprecated private TopicPartitionInfo resolvePartition(TbMsg tbMsg, String queueName) { return mainCtx.resolve(ServiceType.TB_RULE_ENGINE, queueName, getTenantId(), tbMsg.getOriginator()); } private TopicPartitionInfo resolvePartition(TbMsg tbMsg) { - return resolvePartition(tbMsg, tbMsg.getQueueId()); + return resolvePartition(tbMsg, tbMsg.getQueueName()); } private void enqueueForTellNext(TopicPartitionInfo tpi, TbMsg source, Set relationTypes, String failureMessage, Runnable onSuccess, Consumer onFailure) { - enqueueForTellNext(tpi, source.getQueueId(), source, relationTypes, failureMessage, onSuccess, onFailure); + enqueueForTellNext(tpi, source.getQueueName(), source, relationTypes, failureMessage, onSuccess, onFailure); } - private void enqueueForTellNext(TopicPartitionInfo tpi, QueueId queueId, TbMsg source, Set relationTypes, String failureMessage, Runnable onSuccess, Consumer onFailure) { + private void enqueueForTellNext(TopicPartitionInfo tpi, String queueName, TbMsg source, Set relationTypes, String failureMessage, Runnable onSuccess, Consumer onFailure) { if (!source.isValid()) { log.trace("[{}] Skip invalid message: {}", getTenantId(), source); if (onFailure != null) { @@ -261,7 +249,7 @@ class DefaultTbContext implements TbContext { } RuleChainId ruleChainId = nodeCtx.getSelf().getRuleChainId(); RuleNodeId ruleNodeId = nodeCtx.getSelf().getId(); - TbMsg tbMsg = TbMsg.newMsg(source, queueId, ruleChainId, ruleNodeId); + TbMsg tbMsg = TbMsg.newMsg(source, queueName, ruleChainId, ruleNodeId); TransportProtos.ToRuleEngineMsg.Builder msg = TransportProtos.ToRuleEngineMsg.newBuilder() .setTenantIdMSB(getTenantId().getId().getMostSignificantBits()) .setTenantIdLSB(getTenantId().getId().getLeastSignificantBits()) @@ -320,13 +308,13 @@ class DefaultTbContext implements TbContext { } @Override - public TbMsg newMsg(QueueId queueId, String type, EntityId originator, TbMsgMetaData metaData, String data) { - return newMsg(queueId, type, originator, null, metaData, data); + 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(QueueId queueId, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { - return TbMsg.newMsg(queueId, type, originator, customerId, metaData, data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId()); + 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()); } @Override @@ -340,17 +328,17 @@ class DefaultTbContext implements TbContext { public TbMsg deviceCreatedMsg(Device device, RuleNodeId ruleNodeId) { RuleChainId ruleChainId = null; - QueueId queueId = null; + String queueName = null; if (device.getDeviceProfileId() != null) { DeviceProfile deviceProfile = mainCtx.getDeviceProfileCache().find(device.getDeviceProfileId()); if (deviceProfile == null) { log.warn("[{}] Device profile is null!", device.getDeviceProfileId()); } else { ruleChainId = deviceProfile.getDefaultRuleChainId(); - queueId = deviceProfile.getDefaultQueueId(); + queueName = deviceProfile.getDefaultQueueName(); } } - return entityActionMsg(device, device.getId(), ruleNodeId, DataConstants.ENTITY_CREATED, queueId, ruleChainId); + return entityActionMsg(device, device.getId(), ruleNodeId, DataConstants.ENTITY_CREATED, queueName, ruleChainId); } public TbMsg assetCreatedMsg(Asset asset, RuleNodeId ruleNodeId) { @@ -359,7 +347,7 @@ class DefaultTbContext implements TbContext { public TbMsg alarmActionMsg(Alarm alarm, RuleNodeId ruleNodeId, String action) { RuleChainId ruleChainId = null; - QueueId queueId = null; + String queueName = null; if (EntityType.DEVICE.equals(alarm.getOriginator().getEntityType())) { DeviceId deviceId = new DeviceId(alarm.getOriginator().getId()); DeviceProfile deviceProfile = mainCtx.getDeviceProfileCache().get(getTenantId(), deviceId); @@ -367,10 +355,10 @@ class DefaultTbContext implements TbContext { log.warn("[{}] Device profile is null!", deviceId); } else { ruleChainId = deviceProfile.getDefaultRuleChainId(); - queueId = deviceProfile.getDefaultQueueId(); + queueName = deviceProfile.getDefaultQueueName(); } } - return entityActionMsg(alarm, alarm.getId(), ruleNodeId, action, queueId, ruleChainId); + return entityActionMsg(alarm, alarm.getId(), ruleNodeId, action, queueName, ruleChainId); } @Override @@ -382,9 +370,9 @@ class DefaultTbContext implements TbContext { return entityActionMsg(entity, id, ruleNodeId, action, null, null); } - public TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, String action, QueueId queueId, RuleChainId ruleChainId) { + public TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, String action, String queueName, RuleChainId ruleChainId) { try { - return TbMsg.newMsg(queueId, action, id, getActionMetaData(ruleNodeId), mapper.writeValueAsString(mapper.valueToTree(entity)), ruleChainId, null); + return TbMsg.newMsg(queueName, action, id, getActionMetaData(ruleNodeId), mapper.writeValueAsString(mapper.valueToTree(entity)), ruleChainId, null); } catch (JsonProcessingException | IllegalArgumentException e) { throw new RuntimeException("Failed to process " + id.getEntityType().name().toLowerCase() + " " + action + " msg: " + e); } 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 add500f058..b4911650f3 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 @@ -293,7 +293,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor ruleNodeRelations = nodeRoutes.get(originatorNodeId); if (ruleNodeRelations == null) { // When unchecked, this will cause NullPointerException when rule node doesn't exist anymore diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java index 5003aac526..d2e11086c7 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java @@ -126,7 +126,9 @@ public class AlarmController extends BaseController { "\n\nPlatform also deduplicate the alarms based on the entity id of originator and alarm 'type'. " + "For example, if the user or system component create the alarm with the type 'HighTemperature' for device 'Device A' the new active alarm is created. " + "If the user tries to create 'HighTemperature' alarm for the same device again, the previous alarm will be updated (the 'end_ts' will be set to current timestamp). " + - "If the user clears the alarm (see 'Clear Alarm(clearAlarm)'), than new alarm with the same type and same device may be created. " + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH + "If the user clears the alarm (see 'Clear Alarm(clearAlarm)'), than new alarm with the same type and same device may be created. " + + "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Alarm entity. " + + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH , produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm", method = RequestMethod.POST) diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java index f1ff6aa3c6..75e77d4719 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java @@ -139,7 +139,9 @@ public class AssetController extends BaseController { notes = "Creates or Updates the Asset. When creating asset, platform generates Asset Id as " + UUID_WIKI_LINK + "The newly created Asset id will be present in the response. " + "Specify existing Asset id to update the asset. " + - "Referencing non-existing Asset Id will cause 'Not Found' error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE) + "Referencing non-existing Asset Id will cause 'Not Found' error. " + + "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Asset entity. " + + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/asset", method = RequestMethod.POST) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java b/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java index a425b0968a..bdf74c9055 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java +++ b/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java @@ -112,6 +112,10 @@ public class ControllerConstants { protected static final String DEVICE_PROFILE_INFO_DESCRIPTION = "Device Profile Info is a lightweight object that includes main information about Device Profile excluding the heavyweight configuration object. "; protected static final String QUEUE_SERVICE_TYPE_DESCRIPTION = "Service type (implemented only for the TB-RULE-ENGINE)"; protected static final String QUEUE_SERVICE_TYPE_ALLOWABLE_VALUES = "TB-RULE-ENGINE, TB-CORE, TB-TRANSPORT, JS-EXECUTOR"; + protected static final String QUEUE_QUEUE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the queue name."; + protected static final String QUEUE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, topic"; + protected static final String QUEUE_ID_PARAM_DESCRIPTION = "A string value representing the queue id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; + protected static final String QUEUE_NAME_PARAM_DESCRIPTION = "A string value representing the queue id. For example, 'Main'"; protected static final String OTA_PACKAGE_INFO_DESCRIPTION = "OTA Package Info is a lightweight object that includes main information about the OTA Package excluding the heavyweight data. "; protected static final String OTA_PACKAGE_DESCRIPTION = "OTA Package is a heavyweight object that includes main information about the OTA Package and also data. "; protected static final String OTA_PACKAGE_CHECKSUM_ALGORITHM_ALLOWABLE_VALUES = "MD5, SHA256, SHA384, SHA512, CRC32, MURMUR3_32, MURMUR3_128"; diff --git a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java index 2510974991..591eaa1318 100644 --- a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java +++ b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java @@ -138,7 +138,9 @@ public class CustomerController extends BaseController { notes = "Creates or Updates the Customer. When creating customer, platform generates Customer Id as " + UUID_WIKI_LINK + "The newly created Customer Id will be present in the response. " + "Specify existing Customer Id to update the Customer. " + - "Referencing non-existing Customer Id will cause 'Not Found' error." + TENANT_AUTHORITY_PARAGRAPH) + "Referencing non-existing Customer Id will cause 'Not Found' error." + + "Remove 'id', 'tenantId' from the request body example (below) to create new Customer entity. " + + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/customer", method = RequestMethod.POST) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java index 0530f59c97..9ceeb0d14c 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java @@ -172,6 +172,7 @@ public class DashboardController extends BaseController { "The newly created Dashboard id will be present in the response. " + "Specify existing Dashboard id to update the dashboard. " + "Referencing non-existing dashboard Id will cause 'Not Found' error. " + + "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Dashboard entity. " + TENANT_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE) @@ -261,7 +262,7 @@ public class DashboardController extends BaseController { checkParameter(DASHBOARD_ID, strDashboardId); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER); - Set customerIds = customerIdFromStr(strCustomerIds, dashboard); + Set customerIds = customerIdFromStr(strCustomerIds); return tbDashboardService.updateDashboardCustomers(dashboard, customerIds, getCurrentUser()); } @@ -281,7 +282,7 @@ public class DashboardController extends BaseController { checkParameter(DASHBOARD_ID, strDashboardId); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER); - Set customerIds = customerIdFromStr(strCustomerIds, dashboard); + Set customerIds = customerIdFromStr(strCustomerIds); return tbDashboardService.addDashboardCustomers(dashboard, customerIds, getCurrentUser()); } @@ -301,7 +302,7 @@ public class DashboardController extends BaseController { checkParameter(DASHBOARD_ID, strDashboardId); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_CUSTOMER); - Set customerIds = customerIdFromStr(strCustomerIds, dashboard); + Set customerIds = customerIdFromStr(strCustomerIds); return tbDashboardService.removeDashboardCustomers(dashboard, customerIds, getCurrentUser()); } @@ -704,14 +705,11 @@ public class DashboardController extends BaseController { } } - private Set customerIdFromStr(String[] strCustomerIds, Dashboard dashboard) { + private Set customerIdFromStr(String[] strCustomerIds) { Set customerIds = new HashSet<>(); if (strCustomerIds != null) { for (String strCustomerId : strCustomerIds) { - CustomerId customerId = new CustomerId(UUID.fromString(strCustomerId)); - if (dashboard.isAssignedToCustomer(customerId)) { - customerIds.add(customerId); - } + customerIds.add(new CustomerId(UUID.fromString(strCustomerId))); } } return customerIds; diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index 895e711073..ae1558381f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -158,8 +158,9 @@ public class DeviceController extends BaseController { "The newly created device id will be present in the response. " + "Specify existing Device id to update the device. " + "Referencing non-existing device Id will cause 'Not Found' error." + - "\n\nDevice name is unique in the scope of tenant. Use unique identifiers like MAC or IMEI for the device names and non-unique 'label' field for user-friendly visualization purposes." - + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) + "\n\nDevice name is unique in the scope of tenant. Use unique identifiers like MAC or IMEI for the device names and non-unique 'label' field for user-friendly visualization purposes." + + "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Device entity. " + + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/device", method = RequestMethod.POST) @ResponseBody @@ -181,6 +182,7 @@ public class DeviceController extends BaseController { "Requires to provide the Device Credentials object as well. Useful to create device and credentials in one request. " + "You may find the example of LwM2M device and RPK credentials below: \n\n" + DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_DESCRIPTION_MARKDOWN + + "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Device entity. " + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/device-with-credentials", method = RequestMethod.POST) diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java index 08568a28ce..e45a195a2d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java @@ -191,6 +191,7 @@ public class DeviceProfileController extends BaseController { "Specify existing device profile id to update the device profile. " + "Referencing non-existing device profile Id will cause 'Not Found' error. " + NEW_LINE + "Device profile name is unique in the scope of tenant. Only one 'default' device profile may exist in scope of tenant." + DEVICE_PROFILE_DATA + + "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Device Profile entity. " + TENANT_AUTHORITY_PARAGRAPH, produces = "application/json", consumes = "application/json") diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java index 5b2c6a6a9d..59c9eb39c1 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java @@ -141,8 +141,9 @@ public class EdgeController extends BaseController { "The newly created edge id will be present in the response. " + "Specify existing Edge id to update the edge. " + "Referencing non-existing Edge Id will cause 'Not Found' error." + - "\n\nEdge name is unique in the scope of tenant. Use unique identifiers like MAC or IMEI for the edge names and non-unique 'label' field for user-friendly visualization purposes." - + TENANT_AUTHORITY_PARAGRAPH, + "\n\nEdge name is unique in the scope of tenant. Use unique identifiers like MAC or IMEI for the edge names and non-unique 'label' field for user-friendly visualization purposes." + + "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Edge entity. " + + TENANT_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge", method = RequestMethod.POST) diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java index 65b5f10407..f2bfbc9478 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java @@ -192,7 +192,7 @@ public class EntitiesVersionControlController extends BaseController { @GetMapping(value = "/version/{requestId}/status") public VersionCreationResult getVersionCreateRequestStatus(@ApiParam(value = VC_REQUEST_ID_PARAM_DESCRIPTION, required = true) @PathVariable UUID requestId) throws Exception { - accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); + accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.WRITE); return versionControlService.getVersionCreateStatus(getCurrentUser(), requestId); } @@ -469,7 +469,7 @@ public class EntitiesVersionControlController extends BaseController { @GetMapping(value = "/entity/{requestId}/status") public VersionLoadResult getVersionLoadRequestStatus(@ApiParam(value = VC_REQUEST_ID_PARAM_DESCRIPTION, required = true) @PathVariable UUID requestId) throws Exception { - accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); + accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.WRITE); return versionControlService.getVersionLoadStatus(getCurrentUser(), requestId); } diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java index 7d5de91890..0f299fefae 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java @@ -132,7 +132,9 @@ public class EntityViewController extends BaseController { } @ApiOperation(value = "Save or update entity view (saveEntityView)", - notes = ENTITY_VIEW_DESCRIPTION + MODEL_DESCRIPTION, + notes = ENTITY_VIEW_DESCRIPTION + MODEL_DESCRIPTION + + "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Entity View entity." + + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/entityView", method = RequestMethod.POST) diff --git a/application/src/main/java/org/thingsboard/server/controller/QueueController.java b/application/src/main/java/org/thingsboard/server/controller/QueueController.java index 644bee9ff0..3b37e6727f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/QueueController.java +++ b/application/src/main/java/org/thingsboard/server/controller/QueueController.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.controller; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; import lombok.RequiredArgsConstructor; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; @@ -37,6 +39,22 @@ import org.thingsboard.server.service.security.permission.Resource; import java.util.UUID; +import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS; +import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.QUEUE_ID_PARAM_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.QUEUE_NAME_PARAM_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.QUEUE_QUEUE_TEXT_SEARCH_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.QUEUE_SERVICE_TYPE_ALLOWABLE_VALUES; +import static org.thingsboard.server.controller.ControllerConstants.QUEUE_SERVICE_TYPE_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.QUEUE_SORT_PROPERTY_ALLOWABLE_VALUES; +import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES; +import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_AUTHORITY_PARAGRAPH; +import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH; +import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK; + @RestController @TbCoreComponent @RequestMapping("/api") @@ -45,14 +63,23 @@ public class QueueController extends BaseController { private final TbQueueService tbQueueService; + @ApiOperation(value = "Get Queues (getTenantQueuesByServiceType)", + notes = "Returns a page of queues registered in the platform. " + + PAGE_DATA_PARAMETERS + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @RequestMapping(value = "/queues", params = {"serviceType", "pageSize", "page"}, method = RequestMethod.GET) @ResponseBody - public PageData getTenantQueuesByServiceType(@RequestParam String serviceType, + public PageData getTenantQueuesByServiceType(@ApiParam(value = QUEUE_SERVICE_TYPE_DESCRIPTION, allowableValues = QUEUE_SERVICE_TYPE_ALLOWABLE_VALUES, required = true) + @RequestParam String serviceType, + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true) @RequestParam int page, + @ApiParam(value = QUEUE_QUEUE_TEXT_SEARCH_DESCRIPTION) @RequestParam(required = false) String textSearch, + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = QUEUE_SORT_PROPERTY_ALLOWABLE_VALUES) @RequestParam(required = false) String sortProperty, + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { checkParameter("serviceType", serviceType); PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); @@ -65,20 +92,44 @@ public class QueueController extends BaseController { } } + @ApiOperation(value = "Get Queue (getQueueById)", + notes = "Fetch the Queue object based on the provided Queue Id. " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @RequestMapping(value = "/queues/{queueId}", method = RequestMethod.GET) @ResponseBody - public Queue getQueueById(@PathVariable("queueId") String queueIdStr) throws ThingsboardException { + public Queue getQueueById(@ApiParam(value = QUEUE_ID_PARAM_DESCRIPTION) + @PathVariable("queueId") String queueIdStr) throws ThingsboardException { checkParameter("queueId", queueIdStr); QueueId queueId = new QueueId(UUID.fromString(queueIdStr)); checkQueueId(queueId, Operation.READ); return checkNotNull(queueService.findQueueById(getTenantId(), queueId)); } + @ApiOperation(value = "Get Queue (getQueueByName)", + notes = "Fetch the Queue object based on the provided Queue name. " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @RequestMapping(value = "/queues/name/{queueName}", method = RequestMethod.GET) + @ResponseBody + public Queue getQueueByName(@ApiParam(value = QUEUE_NAME_PARAM_DESCRIPTION) + @PathVariable("queueName") String queueName) throws ThingsboardException { + checkParameter("queueName", queueName); + return checkNotNull(queueService.findQueueByTenantIdAndName(getTenantId(), queueName)); + } + + @ApiOperation(value = "Create Or Update Queue (saveQueue)", + notes = "Create or update the Queue. When creating queue, platform generates Queue Id as " + UUID_WIKI_LINK + + "Specify existing Queue id to update the queue. " + + "Referencing non-existing Queue Id will cause 'Not Found' error." + + "\n\nQueue name is unique in the scope of sysadmin. " + + "Remove 'id', 'tenantId' from the request body example (below) to create new Queue entity. " + + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") @RequestMapping(value = "/queues", params = {"serviceType"}, method = RequestMethod.POST) @ResponseBody - public Queue saveQueue(@RequestBody Queue queue, + + public Queue saveQueue(@ApiParam(value = "A JSON value representing the queue.") + @RequestBody Queue queue, + @ApiParam(value = QUEUE_SERVICE_TYPE_DESCRIPTION, allowableValues = QUEUE_SERVICE_TYPE_ALLOWABLE_VALUES, required = true) @RequestParam String serviceType) throws ThingsboardException { checkParameter("serviceType", serviceType); queue.setTenantId(getCurrentUser().getTenantId()); @@ -97,10 +148,12 @@ public class QueueController extends BaseController { } } + @ApiOperation(value = "Delete Queue (deleteQueue)", notes = "Deletes the Queue. " + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") @RequestMapping(value = "/queues/{queueId}", method = RequestMethod.DELETE) @ResponseBody - public void deleteQueue(@PathVariable("queueId") String queueIdStr) throws ThingsboardException { + public void deleteQueue(@ApiParam(value = QUEUE_ID_PARAM_DESCRIPTION) + @PathVariable("queueId") String queueIdStr) throws ThingsboardException { checkParameter("queueId", queueIdStr); QueueId queueId = new QueueId(toUUID(queueIdStr)); checkQueueId(queueId, Operation.DELETE); 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 38e7f5bb7d..faa1711fd9 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -227,7 +227,9 @@ public class RuleChainController extends BaseController { "The newly created Rule Chain Id will be present in the response. " + "Specify existing Rule Chain id to update the rule chain. " + "Referencing non-existing rule chain Id will cause 'Not Found' error." + - "\n\n" + RULE_CHAIN_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH) + "\n\n" + RULE_CHAIN_DESCRIPTION + + "Remove 'id', 'tenantId' from the request body example (below) to create new Rule Chain entity." + + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/ruleChain", method = RequestMethod.POST) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java index 4342a647c5..ec0b530117 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java @@ -139,7 +139,9 @@ public class TbResourceController extends BaseController { "The newly created Resource id will be present in the response. " + "Specify existing Resource id to update the Resource. " + "Referencing non-existing Resource Id will cause 'Not Found' error. " + - "\n\nResource combination of the title with the key is unique in the scope of tenant. " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH, + "\n\nResource combination of the title with the key is unique in the scope of tenant. " + + "Remove 'id', 'tenantId' from the request body example (below) to create new Resource entity." + + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH, produces = "application/json", consumes = "application/json") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") diff --git a/application/src/main/java/org/thingsboard/server/controller/TenantController.java b/application/src/main/java/org/thingsboard/server/controller/TenantController.java index ef3c141131..b44c5327cd 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TenantController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TenantController.java @@ -115,6 +115,7 @@ public class TenantController extends BaseController { "The newly created Tenant Id will be present in the response. " + "Specify existing Tenant Id id to update the Tenant. " + "Referencing non-existing Tenant Id will cause 'Not Found' error." + + "Remove 'id', 'tenantId' from the request body example (below) to create new Tenant entity." + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('SYS_ADMIN')") @RequestMapping(value = "/tenant", method = RequestMethod.POST) diff --git a/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java b/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java index dbdc702696..e5a33da948 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java @@ -128,7 +128,6 @@ public class TenantProfileController extends BaseController { "{\n" + " \"name\": \"Default\",\n" + " \"description\": \"Default tenant profile\",\n" + - " \"isolatedTbCore\": false,\n" + " \"isolatedTbRuleEngine\": false,\n" + " \"profileData\": {\n" + " \"configuration\": {\n" + @@ -165,6 +164,7 @@ public class TenantProfileController extends BaseController { " \"default\": true\n" + "}" + MARKDOWN_CODE_BLOCK_END + + "Remove 'id', from the request body example (below) to create new Tenant Profile entity." + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('SYS_ADMIN')") @RequestMapping(value = "/tenantProfile", method = RequestMethod.POST) diff --git a/application/src/main/java/org/thingsboard/server/controller/TwoFaConfigController.java b/application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthConfigController.java similarity index 99% rename from application/src/main/java/org/thingsboard/server/controller/TwoFaConfigController.java rename to application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthConfigController.java index 4b34b95f59..5aad5c9503 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TwoFaConfigController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthConfigController.java @@ -50,7 +50,7 @@ import static org.thingsboard.server.controller.ControllerConstants.NEW_LINE; @RequestMapping("/api/2fa") @TbCoreComponent @RequiredArgsConstructor -public class TwoFaConfigController extends BaseController { +public class TwoFactorAuthConfigController extends BaseController { private final TwoFaConfigManager twoFaConfigManager; private final TwoFactorAuthService twoFactorAuthService; diff --git a/application/src/main/java/org/thingsboard/server/controller/UserController.java b/application/src/main/java/org/thingsboard/server/controller/UserController.java index e9375665bb..e96a73b65c 100644 --- a/application/src/main/java/org/thingsboard/server/controller/UserController.java +++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java @@ -176,7 +176,9 @@ public class UserController extends BaseController { "The newly created User Id will be present in the response. " + "Specify existing User Id to update the device. " + "Referencing non-existing User Id will cause 'Not Found' error." + - "\n\nDevice email is unique for entire platform setup.") + "\n\nDevice email is unique for entire platform setup." + + "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new User entity." + + "\n\nAvailable for users with 'SYS_ADMIN', 'TENANT_ADMIN' or 'CUSTOMER_USER' authority.") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/user", method = RequestMethod.POST) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java index 71ea889c03..725d8ca2b4 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java @@ -85,8 +85,9 @@ public class WidgetTypeController extends AutoCommitController { "Specify existing Widget Type id to update the Widget Type. " + "Referencing non-existing Widget Type Id will cause 'Not Found' error." + "\n\nWidget Type alias is unique in the scope of Widget Bundle. " + - "Special Tenant Id '13814000-1dd2-11b2-8080-808080808080' is automatically used if the create request is sent by user with 'SYS_ADMIN' authority." - + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) + "Special Tenant Id '13814000-1dd2-11b2-8080-808080808080' is automatically used if the create request is sent by user with 'SYS_ADMIN' authority." + + "Remove 'id', 'tenantId' rom the request body example (below) to create new Widget Type entity." + + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @RequestMapping(value = "/widgetType", method = RequestMethod.POST) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java index 2d51c3dee8..23ec498d0e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java +++ b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java @@ -89,8 +89,9 @@ public class WidgetsBundleController extends BaseController { "Specify existing Widget Bundle id to update the Widget Bundle. " + "Referencing non-existing Widget Bundle Id will cause 'Not Found' error." + "\n\nWidget Bundle alias is unique in the scope of tenant. " + - "Special Tenant Id '13814000-1dd2-11b2-8080-808080808080' is automatically used if the create bundle request is sent by user with 'SYS_ADMIN' authority." - + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) + "Special Tenant Id '13814000-1dd2-11b2-8080-808080808080' is automatically used if the create bundle request is sent by user with 'SYS_ADMIN' authority." + + "Remove 'id', 'tenantId' from the request body example (below) to create new Widgets Bundle entity." + + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @RequestMapping(value = "/widgetsBundle", method = RequestMethod.POST) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardCredentialsExpiredResponse.java b/application/src/main/java/org/thingsboard/server/exception/ThingsboardCredentialsExpiredResponse.java index b02f85f84a..208fdead93 100644 --- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardCredentialsExpiredResponse.java +++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardCredentialsExpiredResponse.java @@ -34,7 +34,7 @@ public class ThingsboardCredentialsExpiredResponse extends ThingsboardErrorRespo return new ThingsboardCredentialsExpiredResponse(message, resetToken); } - @ApiModelProperty(position = 5, value = "Password reset token", readOnly = true) + @ApiModelProperty(position = 5, value = "Password reset token", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public String getResetToken() { return resetToken; } diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java index 09fce98985..7cf2ca87f1 100644 --- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java +++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java @@ -46,12 +46,12 @@ public class ThingsboardErrorResponse { return new ThingsboardErrorResponse(message, errorCode, status); } - @ApiModelProperty(position = 1, value = "HTTP Response Status Code", example = "401", readOnly = true) + @ApiModelProperty(position = 1, value = "HTTP Response Status Code", example = "401", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public Integer getStatus() { return status.value(); } - @ApiModelProperty(position = 2, value = "Error message", example = "Authentication failed", readOnly = true) + @ApiModelProperty(position = 2, value = "Error message", example = "Authentication failed", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public String getMessage() { return message; } @@ -69,12 +69,12 @@ public class ThingsboardErrorResponse { "\n\n* `34` - Too many updates (Too many updates over Websocket session)" + "\n\n* `40` - Subscription violation (HTTP: 403 - Forbidden)", example = "10", dataType = "integer", - readOnly = true) + accessMode = ApiModelProperty.AccessMode.READ_ONLY) public ThingsboardErrorCode getErrorCode() { return errorCode; } - @ApiModelProperty(position = 4, value = "Timestamp", readOnly = true) + @ApiModelProperty(position = 4, value = "Timestamp", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public Date getTimestamp() { return timestamp; } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java index 3920e567f9..4e12fa3366 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java @@ -48,9 +48,8 @@ public class DeviceProfileMsgConstructor { // builder.setDefaultRuleChainIdMSB(deviceProfile.getDefaultRuleChainId().getId().getMostSignificantBits()) // .setDefaultRuleChainIdLSB(deviceProfile.getDefaultRuleChainId().getId().getLeastSignificantBits()); // } - if (deviceProfile.getDefaultQueueId() != null) { - builder.setDefaultQueueIdMSB(deviceProfile.getDefaultQueueId().getId().getMostSignificantBits()) - .setDefaultQueueIdLSB(deviceProfile.getDefaultQueueId().getId().getLeastSignificantBits()); + if (deviceProfile.getDefaultQueueName() != null) { + builder.setDefaultQueueName(deviceProfile.getDefaultQueueName()); } if (deviceProfile.getDescription() != null) { builder.setDescription(deviceProfile.getDescription()); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java index 84cc63d73d..35ac760faa 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.service.edge.rpc.constructor; -import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; @@ -23,7 +22,6 @@ import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; -import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg; @@ -35,11 +33,8 @@ import org.thingsboard.server.service.edge.rpc.constructor.rule.RuleChainMetadat @Component @Slf4j @TbCoreComponent -@AllArgsConstructor public class RuleChainMsgConstructor { - private final QueueService queueService; - public RuleChainUpdateMsg constructRuleChainUpdatedMsg(RuleChainId edgeRootRuleChainId, UpdateMsgType msgType, RuleChain ruleChain) { RuleChainUpdateMsg.Builder builder = RuleChainUpdateMsg.newBuilder() .setMsgType(msgType) @@ -61,7 +56,7 @@ public class RuleChainMsgConstructor { RuleChainMetaData ruleChainMetaData, EdgeVersion edgeVersion) { RuleChainMetadataConstructor ruleChainMetadataConstructor - = RuleChainMetadataConstructorFactory.getByEdgeVersion(edgeVersion, queueService); + = RuleChainMetadataConstructorFactory.getByEdgeVersion(edgeVersion); return ruleChainMetadataConstructor.constructRuleChainMetadataUpdatedMsg(tenantId, msgType, ruleChainMetaData); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/AbstractRuleChainMetadataConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/AbstractRuleChainMetadataConstructor.java index a938d42c3b..8fb32926f5 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/AbstractRuleChainMetadataConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/AbstractRuleChainMetadataConstructor.java @@ -16,20 +16,15 @@ package org.thingsboard.server.service.edge.rpc.constructor.rule; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.rule.engine.flow.TbCheckpointNode; -import org.thingsboard.server.common.data.id.QueueId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.data.rule.NodeConnectionInfo; import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleNode; -import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.gen.edge.v1.NodeConnectionInfoProto; import org.thingsboard.server.gen.edge.v1.RuleChainConnectionInfoProto; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; @@ -39,16 +34,11 @@ import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import java.util.ArrayList; import java.util.List; import java.util.NavigableSet; -import java.util.UUID; @Slf4j @AllArgsConstructor public abstract class AbstractRuleChainMetadataConstructor implements RuleChainMetadataConstructor { - public static final List nodeTypes = List.of(TbCheckpointNode.class.getName()); - - private final QueueService queueService; - @Override public RuleChainMetadataUpdateMsg constructRuleChainMetadataUpdatedMsg(TenantId tenantId, UpdateMsgType msgType, @@ -144,24 +134,4 @@ public abstract class AbstractRuleChainMetadataConstructor implements RuleChainM .setAdditionalInfo(JacksonUtil.OBJECT_MAPPER.writeValueAsString(ruleChainConnectionInfo.getAdditionalInfo())) .build(); } - - protected List updateQueueIdToQueueNameNodeConfiguration(TenantId tenantId, List nodes) { - List result = new ArrayList<>(); - for (RuleNode node : nodes) { - if (nodeTypes.contains(node.getType())) { - ObjectNode configuration = (ObjectNode) node.getConfiguration(); - JsonNode queueIdNode = configuration.remove("queueId"); - if (queueIdNode != null) { - String queueId = queueIdNode.asText(); - Queue queueById = queueService.findQueueById(tenantId, new QueueId(UUID.fromString(queueId))); - if (queueById != null) { - configuration.put("queueName", queueById.getName()); - node.setConfiguration(configuration); - } - } - } - result.add(node); - } - return result; - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorFactory.java index 864a631e96..5fc686f86a 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorFactory.java @@ -15,21 +15,18 @@ */ package org.thingsboard.server.service.edge.rpc.constructor.rule; -import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.gen.edge.v1.EdgeVersion; public final class RuleChainMetadataConstructorFactory { - public static RuleChainMetadataConstructor getByEdgeVersion(EdgeVersion edgeVersion, - QueueService queueService) { + public static RuleChainMetadataConstructor getByEdgeVersion(EdgeVersion edgeVersion) { switch (edgeVersion) { case V_3_3_0: - return new RuleChainMetadataConstructorV330(queueService); + return new RuleChainMetadataConstructorV330(); case V_3_3_3: - return new RuleChainMetadataConstructorV333(queueService); case V_3_4_0: default: - return new RuleChainMetadataConstructorV340(queueService); + return new RuleChainMetadataConstructorV340(); } } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV330.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV330.java index 4c29164f5a..3172ee9277 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV330.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV330.java @@ -27,7 +27,6 @@ import org.thingsboard.server.common.data.rule.NodeConnectionInfo; import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleNode; -import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import java.util.ArrayList; @@ -43,16 +42,11 @@ public class RuleChainMetadataConstructorV330 extends AbstractRuleChainMetadataC private static final String RULE_CHAIN_INPUT_NODE = TbRuleChainInputNode.class.getName(); private static final String TB_RULE_CHAIN_OUTPUT_NODE = TbRuleChainOutputNode.class.getName(); - public RuleChainMetadataConstructorV330(QueueService queueService) { - super(queueService); - } - @Override protected void constructRuleChainMetadataUpdatedMsg(TenantId tenantId, RuleChainMetadataUpdateMsg.Builder builder, RuleChainMetaData ruleChainMetaData) throws JsonProcessingException { List supportedNodes = filterNodes(ruleChainMetaData.getNodes()); - supportedNodes = updateQueueIdToQueueNameNodeConfiguration(tenantId, supportedNodes); NavigableSet removedNodeIndexes = getRemovedNodeIndexes(ruleChainMetaData.getNodes(), ruleChainMetaData.getConnections()); List connections = filterConnections(ruleChainMetaData.getNodes(), ruleChainMetaData.getConnections(), removedNodeIndexes); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV333.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV333.java deleted file mode 100644 index 3fb346e701..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV333.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright © 2016-2022 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.edge.rpc.constructor.rule; - -import com.fasterxml.jackson.core.JsonProcessingException; -import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.rule.RuleChainMetaData; -import org.thingsboard.server.common.data.rule.RuleNode; -import org.thingsboard.server.dao.queue.QueueService; -import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; - -import java.util.List; -import java.util.TreeSet; - -@Slf4j -public class RuleChainMetadataConstructorV333 extends AbstractRuleChainMetadataConstructor { - - public RuleChainMetadataConstructorV333(QueueService queueService) { - super(queueService); - } - - @Override - protected void constructRuleChainMetadataUpdatedMsg(TenantId tenantId, - RuleChainMetadataUpdateMsg.Builder builder, - RuleChainMetaData ruleChainMetaData) throws JsonProcessingException { - List nodes = updateQueueIdToQueueNameNodeConfiguration(tenantId, ruleChainMetaData.getNodes()); - builder.addAllNodes(constructNodes(nodes)) - .addAllConnections(constructConnections(ruleChainMetaData.getConnections())) - .addAllRuleChainConnections(constructRuleChainConnections(ruleChainMetaData.getRuleChainConnections(), new TreeSet<>())); - if (ruleChainMetaData.getFirstNodeIndex() != null) { - builder.setFirstNodeIndex(ruleChainMetaData.getFirstNodeIndex()); - } else { - builder.setFirstNodeIndex(-1); - } - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV340.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV340.java index edb6260cc5..9acb2ae9f5 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV340.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV340.java @@ -19,7 +19,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.rule.RuleChainMetaData; -import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import java.util.TreeSet; @@ -27,10 +26,6 @@ import java.util.TreeSet; @Slf4j public class RuleChainMetadataConstructorV340 extends AbstractRuleChainMetadataConstructor { - public RuleChainMetadataConstructorV340(QueueService queueService) { - super(queueService); - } - @Override protected void constructRuleChainMetadataUpdatedMsg(TenantId tenantId, RuleChainMetadataUpdateMsg.Builder builder, diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryEdgeProcessor.java index f30111e1ef..ad6446cb95 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryEdgeProcessor.java @@ -158,21 +158,21 @@ public class TelemetryEdgeProcessor extends BaseEdgeProcessor { return metaData; } - private Pair getDefaultQueueNameAndRuleChainId(TenantId tenantId, EntityId entityId) { + private Pair getDefaultQueueNameAndRuleChainId(TenantId tenantId, EntityId entityId) { if (EntityType.DEVICE.equals(entityId.getEntityType())) { DeviceProfile deviceProfile = deviceProfileCache.get(tenantId, new DeviceId(entityId.getId())); RuleChainId ruleChainId; - QueueId queueId; + String queueName; if (deviceProfile == null) { log.warn("[{}] Device profile is null!", entityId); ruleChainId = null; - queueId = null; + queueName = null; } else { ruleChainId = deviceProfile.getDefaultRuleChainId(); - queueId = deviceProfile.getDefaultQueueId(); + queueName = deviceProfile.getDefaultQueueName(); } - return new ImmutablePair<>(queueId, ruleChainId); + return new ImmutablePair<>(queueName, ruleChainId); } else { return new ImmutablePair<>(null, null); } @@ -183,10 +183,8 @@ public class TelemetryEdgeProcessor extends BaseEdgeProcessor { for (TransportProtos.TsKvListProto tsKv : msg.getTsKvListList()) { JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList()); metaData.putValue("ts", tsKv.getTs() + ""); - Pair defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); - QueueId queueId = defaultQueueAndRuleChain.getKey(); - RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue(); - TbMsg tbMsg = TbMsg.newMsg(queueId, SessionMsgType.POST_TELEMETRY_REQUEST.name(), entityId, customerId, metaData, gson.toJson(json), ruleChainId, null); + var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); + TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), SessionMsgType.POST_TELEMETRY_REQUEST.name(), entityId, customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null); tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { @Override public void onSuccess(TbQueueMsgMetadata metadata) { @@ -206,10 +204,8 @@ public class TelemetryEdgeProcessor extends BaseEdgeProcessor { private ListenableFuture processPostAttributes(TenantId tenantId, CustomerId customerId, EntityId entityId, TransportProtos.PostAttributeMsg msg, TbMsgMetaData metaData) { SettableFuture futureToSet = SettableFuture.create(); JsonObject json = JsonUtils.getJsonObject(msg.getKvList()); - Pair defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); - QueueId queueId = defaultQueueAndRuleChain.getKey(); - RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue(); - TbMsg tbMsg = TbMsg.newMsg(queueId, SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), entityId, customerId, metaData, gson.toJson(json), ruleChainId, null); + var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); + TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), entityId, customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null); tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { @Override public void onSuccess(TbQueueMsgMetadata metadata) { @@ -233,10 +229,8 @@ public class TelemetryEdgeProcessor extends BaseEdgeProcessor { Futures.addCallback(future, new FutureCallback<>() { @Override public void onSuccess(@Nullable List keys) { - Pair defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); - QueueId queueId = defaultQueueAndRuleChain.getKey(); - RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue(); - TbMsg tbMsg = TbMsg.newMsg(queueId, DataConstants.ATTRIBUTES_UPDATED, entityId, customerId, metaData, gson.toJson(json), ruleChainId, null); + var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); + TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), DataConstants.ATTRIBUTES_UPDATED, entityId, customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null); tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { @Override public void onSuccess(TbQueueMsgMetadata metadata) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java index 21f3171d3d..e66d7d5595 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java @@ -72,6 +72,7 @@ import org.thingsboard.server.gen.edge.v1.RelationRequestMsg; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataRequestMsg; import org.thingsboard.server.gen.edge.v1.UserCredentialsRequestMsg; import org.thingsboard.server.gen.edge.v1.WidgetBundleTypesRequestMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.entityview.TbEntityViewService; import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.state.DefaultDeviceStateService; @@ -83,6 +84,7 @@ import java.util.Map; import java.util.UUID; @Service +@TbCoreComponent @Slf4j public class DefaultEdgeRequestsService implements EdgeRequestsService { diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java index 741a89749f..5d9b4290f4 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java @@ -183,11 +183,17 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement TenantId tenantId = dashboard.getTenantId(); DashboardId dashboardId = dashboard.getId(); try { - if (customerIds.isEmpty()) { + Set addedCustomerIds = new HashSet<>(); + for (CustomerId customerId : customerIds) { + if (!dashboard.isAssignedToCustomer(customerId)) { + addedCustomerIds.add(customerId); + } + } + if (addedCustomerIds.isEmpty()) { return dashboard; } else { Dashboard savedDashboard = null; - for (CustomerId customerId : customerIds) { + for (CustomerId customerId : addedCustomerIds) { savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(tenantId, dashboardId, customerId)); ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId); notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, dashboardId, customerId, savedDashboard, @@ -207,11 +213,17 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement TenantId tenantId = dashboard.getTenantId(); DashboardId dashboardId = dashboard.getId(); try { - if (customerIds.isEmpty()) { + Set removedCustomerIds = new HashSet<>(); + for (CustomerId customerId : customerIds) { + if (dashboard.isAssignedToCustomer(customerId)) { + removedCustomerIds.add(customerId); + } + } + if (removedCustomerIds.isEmpty()) { return dashboard; } else { Dashboard savedDashboard = null; - for (CustomerId customerId : customerIds) { + for (CustomerId customerId : removedCustomerIds) { ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId); savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(tenantId, dashboardId, customerId)); notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, dashboardId, customerId, savedDashboard, diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java index ad0a91e0eb..4355d998bb 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java @@ -48,13 +48,11 @@ import java.util.stream.Collectors; @TbCoreComponent @AllArgsConstructor public class DefaultTbQueueService extends AbstractTbEntityService implements TbQueueService { - private static final String MAIN = "Main"; private static final long DELETE_DELAY = 30; private final QueueService queueService; private final TbClusterService tbClusterService; private final TbQueueAdmin tbQueueAdmin; - private final DeviceProfileService deviceProfileService; private final SchedulerComponent scheduler; @Override @@ -205,35 +203,7 @@ public class DefaultTbQueueService extends AbstractTbEntityService implements Tb } tenantIds.forEach(tenantId -> { - Map> deviceProfileQueues; - - if (oldTenantProfile != null && !newTenantProfile.getId().equals(oldTenantProfile.getId()) || !toRemove.isEmpty()) { - List deviceProfiles = deviceProfileService.findDeviceProfiles(tenantId, new PageLink(Integer.MAX_VALUE)).getData(); - deviceProfileQueues = deviceProfiles.stream() - .filter(dp -> dp.getDefaultQueueId() != null) - .collect(Collectors.groupingBy(DeviceProfile::getDefaultQueueId)); - } else { - deviceProfileQueues = Collections.emptyMap(); - } - - Map createdQueues = toCreate.stream() - .map(key -> saveQueue(new Queue(tenantId, newQueues.get(key)))) - .collect(Collectors.toMap(Queue::getName, Queue::getId)); - - // assigning created queues to device profiles instead of system queues - if (oldTenantProfile != null && !oldTenantProfile.isIsolatedTbRuleEngine()) { - deviceProfileQueues.forEach((queueId, list) -> { - Queue queue = queueService.findQueueById(TenantId.SYS_TENANT_ID, queueId); - QueueId queueIdToAssign = createdQueues.get(queue.getName()); - if (queueIdToAssign == null) { - queueIdToAssign = createdQueues.get(MAIN); - } - for (DeviceProfile deviceProfile : list) { - deviceProfile.setDefaultQueueId(queueIdToAssign); - saveDeviceProfile(deviceProfile); - } - }); - } + toCreate.forEach(key -> saveQueue(new Queue(tenantId, newQueues.get(key)))); toUpdate.forEach(key -> { Queue queueToUpdate = new Queue(tenantId, newQueues.get(key)); @@ -241,9 +211,7 @@ public class DefaultTbQueueService extends AbstractTbEntityService implements Tb queueToUpdate.setId(foundQueue.getId()); queueToUpdate.setCreatedTime(foundQueue.getCreatedTime()); - if (queueToUpdate.equals(foundQueue)) { - //Queue not changed - } else { + if (!queueToUpdate.equals(foundQueue)) { saveQueue(queueToUpdate); } }); @@ -251,26 +219,9 @@ public class DefaultTbQueueService extends AbstractTbEntityService implements Tb toRemove.forEach(q -> { Queue queue = queueService.findQueueByTenantIdAndNameInternal(tenantId, q); QueueId queueIdForRemove = queue.getId(); - if (deviceProfileQueues.containsKey(queueIdForRemove)) { - Queue foundQueue = queueService.findQueueByTenantIdAndName(tenantId, q); - if (foundQueue == null || queue.equals(foundQueue)) { - foundQueue = queueService.findQueueByTenantIdAndName(tenantId, MAIN); - } - QueueId newQueueId = foundQueue.getId(); - deviceProfileQueues.get(queueIdForRemove).stream() - .peek(dp -> dp.setDefaultQueueId(newQueueId)) - .forEach(this::saveDeviceProfile); - } deleteQueue(tenantId, queueIdForRemove); }); }); } - //TODO: remove after implementing TbDeviceProfileService - private void saveDeviceProfile(DeviceProfile deviceProfile) { - DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); - tbClusterService.onDeviceProfileChange(savedDeviceProfile, null); - tbClusterService.broadcastEntityStateChangeEvent(deviceProfile.getTenantId(), savedDeviceProfile.getId(), ComponentLifecycleEvent.UPDATED); - } - } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java index e465e70ad7..a41bbb4385 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java @@ -72,7 +72,7 @@ public class DefaultUserService extends AbstractTbEntityService implements TbUse savedUser, user, actionType, true, null); return savedUser; } catch (Exception e) { - notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.USER), user, actionType, user, e); + notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.USER), tbUser, actionType, user, e); throw e; } } diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java index 14e49e056f..78e03ffe1a 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java @@ -195,22 +195,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { public void createDefaultTenantProfiles() throws Exception { tenantProfileService.findOrCreateDefaultTenantProfile(TenantId.SYS_TENANT_ID); - TenantProfileData tenantProfileData = new TenantProfileData(); - tenantProfileData.setConfiguration(new DefaultTenantProfileConfiguration()); - - TenantProfile isolatedTbCoreProfile = new TenantProfile(); - isolatedTbCoreProfile.setDefault(false); - isolatedTbCoreProfile.setName("Isolated TB Core"); - isolatedTbCoreProfile.setDescription("Isolated TB Core tenant profile"); - isolatedTbCoreProfile.setIsolatedTbCore(true); - isolatedTbCoreProfile.setIsolatedTbRuleEngine(false); - isolatedTbCoreProfile.setProfileData(tenantProfileData); - try { - tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, isolatedTbCoreProfile); - } catch (DataValidationException e) { - log.warn(e.getMessage()); - } - TenantProfileData isolatedRuleEngineTenantProfileData = new TenantProfileData(); isolatedRuleEngineTenantProfileData.setConfiguration(new DefaultTenantProfileConfiguration()); @@ -239,7 +223,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { isolatedTbRuleEngineProfile.setDefault(false); isolatedTbRuleEngineProfile.setName("Isolated TB Rule Engine"); isolatedTbRuleEngineProfile.setDescription("Isolated TB Rule Engine tenant profile"); - isolatedTbRuleEngineProfile.setIsolatedTbCore(false); isolatedTbRuleEngineProfile.setIsolatedTbRuleEngine(true); isolatedTbRuleEngineProfile.setProfileData(isolatedRuleEngineTenantProfileData); @@ -248,20 +231,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { } catch (DataValidationException e) { log.warn(e.getMessage()); } - - TenantProfile isolatedTbCoreAndTbRuleEngineProfile = new TenantProfile(); - isolatedTbCoreAndTbRuleEngineProfile.setDefault(false); - isolatedTbCoreAndTbRuleEngineProfile.setName("Isolated TB Core and TB Rule Engine"); - isolatedTbCoreAndTbRuleEngineProfile.setDescription("Isolated TB Core and TB Rule Engine tenant profile"); - isolatedTbCoreAndTbRuleEngineProfile.setIsolatedTbCore(true); - isolatedTbCoreAndTbRuleEngineProfile.setIsolatedTbRuleEngine(true); - isolatedTbCoreAndTbRuleEngineProfile.setProfileData(isolatedRuleEngineTenantProfileData); - - try { - tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, isolatedTbCoreAndTbRuleEngineProfile); - } catch (DataValidationException e) { - log.warn(e.getMessage()); - } } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java index e428d55d91..d22039683d 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java @@ -589,10 +589,6 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService } catch (Exception e) { } - log.info("Updating device profiles..."); - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.3.4", "schema_update_device_profile.sql"); - loadSql(schemaUpdateFile, conn); - log.info("Updating schema settings..."); conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3004000;"); log.info("Schema updated."); diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java index 9bfa86b3ba..b90da586fc 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java @@ -156,9 +156,8 @@ public class DefaultDataUpdateService implements DataUpdateService { break; case "3.3.4": log.info("Updating data from version 3.3.4 to 3.4.0 ..."); - rateLimitsUpdater.updateEntities(); tenantsProfileQueueConfigurationUpdater.updateEntities(); - checkPointRuleNodesUpdater.updateEntities(); + rateLimitsUpdater.updateEntities(); break; default: throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion); @@ -629,47 +628,4 @@ public class DefaultDataUpdateService implements DataUpdateService { return mainQueueConfiguration; } - private final PaginatedUpdater checkPointRuleNodesUpdater = - new PaginatedUpdater<>() { - - @Override - protected String getName() { - return "Checkpoint rule nodes updater"; - } - - @Override - protected boolean forceReportTotal() { - return true; - } - - @Override - protected PageData findEntities(String id, PageLink pageLink) { - return ruleChainService.findAllRuleNodesByType("org.thingsboard.rule.engine.flow.TbCheckpointNode", pageLink); - } - - @Override - protected void updateEntity(RuleNode ruleNode) { - updateCheckPointRuleNodeConfiguration(ruleNode); - } - }; - - private void updateCheckPointRuleNodeConfiguration(RuleNode node) { - try { - ObjectNode configuration = (ObjectNode) node.getConfiguration(); - JsonNode queueNameNode = configuration.remove("queueName"); - if (queueNameNode != null) { - RuleChain ruleChain = this.ruleChainService.findRuleChainById(TenantId.SYS_TENANT_ID, node.getRuleChainId()); - TenantId tenantId = ruleChain.getTenantId(); - Map queues = - queueService.findQueuesByTenantId(tenantId).stream().collect(Collectors.toMap(Queue::getName, Queue::getId)); - String queueName = queueNameNode.asText(); - QueueId queueId = queues.get(queueName); - configuration.put("queueId", queueId != null ? queueId.toString() : ""); - ruleChainService.saveRuleNode(tenantId, node); - } - } catch (Exception e) { - log.error("Failed to update checkpoint rule node configuration name=["+node.getName()+"], id=["+ node.getId().getId() +"]", e); - } - } - } diff --git a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java index 7e1a3d7fa7..aa8e019317 100644 --- a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java +++ b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java @@ -20,7 +20,9 @@ import freemarker.template.Configuration; import freemarker.template.Template; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Lazy; import org.springframework.core.NestedRuntimeException; @@ -47,13 +49,17 @@ import org.thingsboard.server.queue.usagestats.TbApiUsageClient; import org.thingsboard.server.service.apiusage.TbApiUsageStateService; import javax.annotation.PostConstruct; -import javax.mail.MessagingException; +import javax.annotation.PreDestroy; import javax.mail.internet.MimeMessage; import java.io.ByteArrayInputStream; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Properties; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; @Service @Slf4j @@ -70,6 +76,8 @@ public class DefaultMailService implements MailService { private final AdminSettingsService adminSettingsService; private final TbApiUsageClient apiUsageClient; + private static final long DEFAULT_TIMEOUT = 10_000; + @Lazy @Autowired private TbApiUsageStateService apiUsageStateService; @@ -77,10 +85,15 @@ public class DefaultMailService implements MailService { @Autowired private MailExecutorService mailExecutorService; + @Autowired + private PasswordResetExecutorService passwordResetExecutorService; + private JavaMailSenderImpl mailSender; private String mailFrom; + private long timeout; + public DefaultMailService(MessageSource messages, Configuration freemarkerConfig, AdminSettingsService adminSettingsService, TbApiUsageClient apiUsageClient) { this.messages = messages; this.freemarkerConfig = freemarkerConfig; @@ -100,6 +113,7 @@ public class DefaultMailService implements MailService { JsonNode jsonConfig = settings.getJsonValue(); mailSender = createMailSender(jsonConfig); mailFrom = jsonConfig.get("mailFrom").asText(); + timeout = jsonConfig.get("timeout").asLong(DEFAULT_TIMEOUT); } else { throw new IncorrectParameterException("Failed to update mail configuration. Settings not found!"); } @@ -166,7 +180,7 @@ public class DefaultMailService implements MailService { @Override public void sendEmail(TenantId tenantId, String email, String subject, String message) throws ThingsboardException { - sendMail(mailSender, mailFrom, email, subject, message); + sendMail(mailSender, mailFrom, email, subject, message, timeout); } @Override @@ -174,13 +188,14 @@ public class DefaultMailService implements MailService { JavaMailSenderImpl testMailSender = createMailSender(jsonConfig); String mailFrom = jsonConfig.get("mailFrom").asText(); String subject = messages.getMessage("test.message.subject", null, Locale.US); + long timeout = jsonConfig.get("timeout").asLong(DEFAULT_TIMEOUT); Map model = new HashMap<>(); model.put(TARGET_EMAIL, email); String message = mergeTemplateIntoString("test.ftl", model); - sendMail(testMailSender, mailFrom, email, subject, message); + sendMail(testMailSender, mailFrom, email, subject, message, timeout); } @Override @@ -194,7 +209,7 @@ public class DefaultMailService implements MailService { String message = mergeTemplateIntoString("activation.ftl", model); - sendMail(mailSender, mailFrom, email, subject, message); + sendMail(mailSender, mailFrom, email, subject, message, timeout); } @Override @@ -208,7 +223,7 @@ public class DefaultMailService implements MailService { String message = mergeTemplateIntoString("account.activated.ftl", model); - sendMail(mailSender, mailFrom, email, subject, message); + sendMail(mailSender, mailFrom, email, subject, message, timeout); } @Override @@ -222,12 +237,12 @@ public class DefaultMailService implements MailService { String message = mergeTemplateIntoString("reset.password.ftl", model); - sendMail(mailSender, mailFrom, email, subject, message); + sendMail(mailSender, mailFrom, email, subject, message, timeout); } @Override public void sendResetPasswordEmailAsync(String passwordResetLink, String email) { - mailExecutorService.execute(() -> { + passwordResetExecutorService.execute(() -> { try { this.sendResetPasswordEmail(passwordResetLink, email); } catch (ThingsboardException e) { @@ -247,20 +262,20 @@ public class DefaultMailService implements MailService { String message = mergeTemplateIntoString("password.was.reset.ftl", model); - sendMail(mailSender, mailFrom, email, subject, message); + sendMail(mailSender, mailFrom, email, subject, message, timeout); } @Override public void send(TenantId tenantId, CustomerId customerId, TbEmail tbEmail) throws ThingsboardException { - sendMail(tenantId, customerId, tbEmail, this.mailSender); + sendMail(tenantId, customerId, tbEmail, this.mailSender, timeout); } @Override - public void send(TenantId tenantId, CustomerId customerId, TbEmail tbEmail, JavaMailSender javaMailSender) throws ThingsboardException { - sendMail(tenantId, customerId, tbEmail, javaMailSender); + public void send(TenantId tenantId, CustomerId customerId, TbEmail tbEmail, JavaMailSender javaMailSender, long timeout) throws ThingsboardException { + sendMail(tenantId, customerId, tbEmail, javaMailSender, timeout); } - private void sendMail(TenantId tenantId, CustomerId customerId, TbEmail tbEmail, JavaMailSender javaMailSender) throws ThingsboardException { + private void sendMail(TenantId tenantId, CustomerId customerId, TbEmail tbEmail, JavaMailSender javaMailSender, long timeout) throws ThingsboardException { if (apiUsageStateService.getApiUsageState(tenantId).isEmailSendEnabled()) { try { MimeMessage mailMsg = javaMailSender.createMimeMessage(); @@ -287,7 +302,7 @@ public class DefaultMailService implements MailService { helper.addInline(imgId, iss, contentType); } } - javaMailSender.send(helper.getMimeMessage()); + sendMailWithTimeout(javaMailSender, helper.getMimeMessage(), timeout); apiUsageClient.report(tenantId, customerId, ApiUsageRecordKey.EMAIL_EXEC_COUNT, 1); } catch (Exception e) { throw handleException(e); @@ -308,7 +323,7 @@ public class DefaultMailService implements MailService { String message = mergeTemplateIntoString("account.lockout.ftl", model); - sendMail(mailSender, mailFrom, email, subject, message); + sendMail(mailSender, mailFrom, email, subject, message, timeout); } @Override @@ -320,7 +335,7 @@ public class DefaultMailService implements MailService { "expirationTimeSeconds", expirationTimeSeconds )); - sendMail(mailSender, mailFrom, email, subject, message); + sendMail(mailSender, mailFrom, email, subject, message, timeout); } @Override @@ -347,7 +362,7 @@ public class DefaultMailService implements MailService { message = mergeTemplateIntoString("state.disabled.ftl", model); break; } - sendMail(mailSender, mailFrom, email, subject, message); + sendMail(mailSender, mailFrom, email, subject, message, timeout); } @Override @@ -447,9 +462,8 @@ public class DefaultMailService implements MailService { } } - private void sendMail(JavaMailSenderImpl mailSender, - String mailFrom, String email, - String subject, String message) throws ThingsboardException { + private void sendMail(JavaMailSenderImpl mailSender, String mailFrom, String email, + String subject, String message, long timeout) throws ThingsboardException { try { MimeMessage mimeMsg = mailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(mimeMsg, UTF_8); @@ -457,12 +471,24 @@ public class DefaultMailService implements MailService { helper.setTo(email); helper.setSubject(subject); helper.setText(message, true); - mailSender.send(helper.getMimeMessage()); + + sendMailWithTimeout(mailSender, helper.getMimeMessage(), timeout); } catch (Exception e) { throw handleException(e); } } + private void sendMailWithTimeout(JavaMailSender mailSender, MimeMessage msg, long timeout) { + try { + mailExecutorService.submit(() -> mailSender.send(msg)).get(timeout, TimeUnit.MILLISECONDS); + } catch (TimeoutException e) { + log.debug("Error during mail submission", e); + throw new RuntimeException("Timeout!"); + } catch (Exception e) { + throw new RuntimeException(ExceptionUtils.getRootCause(e)); + } + } + private String mergeTemplateIntoString(String templateLocation, Map model) throws ThingsboardException { try { diff --git a/msa/js-executor/api/httpServer.js b/application/src/main/java/org/thingsboard/server/service/mail/PasswordResetExecutorService.java similarity index 53% rename from msa/js-executor/api/httpServer.js rename to application/src/main/java/org/thingsboard/server/service/mail/PasswordResetExecutorService.java index 62bf5807b1..ea761c144d 100644 --- a/msa/js-executor/api/httpServer.js +++ b/application/src/main/java/org/thingsboard/server/service/mail/PasswordResetExecutorService.java @@ -1,4 +1,4 @@ -/* +/** * Copyright © 2016-2022 The Thingsboard Authors * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,18 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -const config = require('config'), - logger = require('../config/logger')._logger('httpServer'), - express = require('express'); +package org.thingsboard.server.service.mail; -const httpPort = Number(config.get('http_port')); +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.AbstractListeningExecutor; -const app = express(); +@Component +public class PasswordResetExecutorService extends AbstractListeningExecutor { -app.get('/livenessProbe', async (req, res) => { - const date = new Date(); - const message = { now: date.toISOString() }; - res.send(message); -}) + @Value("${actors.rule.mail_password_reset_thread_pool_size:10}") + private int mailExecutorThreadPoolSize; -app.listen(httpPort, () => logger.info(`Started http endpoint on port ${httpPort}. Please, use /livenessProbe !`)) \ No newline at end of file + @Override + protected int getThreadPollSize() { + return mailExecutorThreadPoolSize; + } + +} 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 9e60f69620..062e4128d4 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 @@ -181,7 +181,7 @@ public class DefaultTbClusterService implements TbClusterService { tbMsg = transformMsg(tbMsg, deviceProfileCache.get(tenantId, new DeviceProfileId(entityId.getId()))); } } - TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tbMsg.getQueueId(), tenantId, entityId); + TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tbMsg.getQueueName(), tenantId, entityId); log.trace("PUSHING msg: {} to:{}", tbMsg, tpi); ToRuleEngineMsg msg = ToRuleEngineMsg.newBuilder() .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) @@ -194,16 +194,16 @@ public class DefaultTbClusterService implements TbClusterService { private TbMsg transformMsg(TbMsg tbMsg, DeviceProfile deviceProfile) { if (deviceProfile != null) { RuleChainId targetRuleChainId = deviceProfile.getDefaultRuleChainId(); - QueueId targetQueueId = deviceProfile.getDefaultQueueId(); + String targetQueueName = deviceProfile.getDefaultQueueName(); boolean isRuleChainTransform = targetRuleChainId != null && !targetRuleChainId.equals(tbMsg.getRuleChainId()); - boolean isQueueTransform = targetQueueId != null && !targetQueueId.equals(tbMsg.getQueueId()); + boolean isQueueTransform = targetQueueName != null && !targetQueueName.equals(tbMsg.getQueueName()); if (isRuleChainTransform && isQueueTransform) { - tbMsg = TbMsg.transformMsg(tbMsg, targetRuleChainId, targetQueueId); + tbMsg = TbMsg.transformMsg(tbMsg, targetRuleChainId, targetQueueName); } else if (isRuleChainTransform) { tbMsg = TbMsg.transformMsg(tbMsg, targetRuleChainId); } else if (isQueueTransform) { - tbMsg = TbMsg.transformMsg(tbMsg, targetQueueId); + tbMsg = TbMsg.transformMsg(tbMsg, targetQueueName); } } return tbMsg; diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java index 92dd4fcf5c..3c7552ff9b 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java @@ -274,7 +274,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< final boolean timeout = !ctx.await(configuration.getPackProcessingTimeout(), TimeUnit.MILLISECONDS); - TbRuleEngineProcessingResult result = new TbRuleEngineProcessingResult(configuration.getId(), timeout, ctx); + TbRuleEngineProcessingResult result = new TbRuleEngineProcessingResult(configuration.getName(), timeout, ctx); if (timeout) { printFirstOrAll(configuration, ctx, ctx.getPendingMap(), "Timeout"); } @@ -339,7 +339,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< new TbMsgPackCallback(id, tenantId, ctx); try { if (toRuleEngineMsg.getTbMsg() != null && !toRuleEngineMsg.getTbMsg().isEmpty()) { - forwardToRuleEngineActor(configuration.getId(), tenantId, toRuleEngineMsg, callback); + forwardToRuleEngineActor(configuration.getName(), tenantId, toRuleEngineMsg, callback); } else { callback.onSuccess(); } @@ -353,7 +353,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< log.info("{} to process [{}] messages", prefix, map.size()); for (Map.Entry> pending : map.entrySet()) { ToRuleEngineMsg tmp = pending.getValue().getValue(); - TbMsg tmpMsg = TbMsg.fromBytes(configuration.getId(), tmp.getTbMsg().toByteArray(), TbMsgCallback.EMPTY); + TbMsg tmpMsg = TbMsg.fromBytes(configuration.getName(), tmp.getTbMsg().toByteArray(), TbMsgCallback.EMPTY); RuleNodeInfo ruleNodeInfo = ctx.getLastVisitedRuleNode(pending.getKey()); if (printAll) { log.trace("[{}] {} to process message: {}, Last Rule Node: {}", TenantId.fromUUID(new UUID(tmp.getTenantIdMSB(), tmp.getTenantIdLSB())), prefix, tmpMsg, ruleNodeInfo); @@ -461,8 +461,8 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< partitionService.removeQueue(queueDeleteMsg); } - private void forwardToRuleEngineActor(QueueId queueId, TenantId tenantId, ToRuleEngineMsg toRuleEngineMsg, TbMsgCallback callback) { - TbMsg tbMsg = TbMsg.fromBytes(queueId, toRuleEngineMsg.getTbMsg().toByteArray(), callback); + private void forwardToRuleEngineActor(String queueName, TenantId tenantId, ToRuleEngineMsg toRuleEngineMsg, TbMsgCallback callback) { + TbMsg tbMsg = TbMsg.fromBytes(queueName, toRuleEngineMsg.getTbMsg().toByteArray(), callback); QueueToRuleEngineMsg msg; ProtocolStringList relationTypesList = toRuleEngineMsg.getRelationTypesList(); Set relationTypes = null; diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTenantRoutingInfoService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTenantRoutingInfoService.java index 75fbd39a95..1ab9686d6f 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTenantRoutingInfoService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTenantRoutingInfoService.java @@ -45,7 +45,7 @@ public class DefaultTenantRoutingInfoService implements TenantRoutingInfoService Tenant tenant = tenantService.findTenantById(tenantId); if (tenant != null) { TenantProfile tenantProfile = tenantProfileCache.get(tenant.getTenantProfileId()); - return new TenantRoutingInfo(tenantId, tenantProfile.isIsolatedTbCore(), tenantProfile.isIsolatedTbRuleEngine()); + return new TenantRoutingInfo(tenantId, tenantProfile.isIsolatedTbRuleEngine()); } else { throw new RuntimeException("Tenant not found!"); } diff --git a/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingResult.java b/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingResult.java index 2e3fedb766..001f4a4c2d 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingResult.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingResult.java @@ -29,7 +29,7 @@ import java.util.concurrent.ConcurrentMap; public class TbRuleEngineProcessingResult { @Getter - private final QueueId queueId; + private final String queueName; @Getter private final boolean success; @Getter @@ -37,8 +37,8 @@ public class TbRuleEngineProcessingResult { @Getter private final TbMsgPackProcessingContext ctx; - public TbRuleEngineProcessingResult(QueueId queueId, boolean timeout, TbMsgPackProcessingContext ctx) { - this.queueId = queueId; + public TbRuleEngineProcessingResult(String queueName, boolean timeout, TbMsgPackProcessingContext ctx) { + this.queueName = queueName; this.timeout = timeout; this.ctx = ctx; this.success = !timeout && ctx.getPendingMap().isEmpty() && ctx.getFailedMap().isEmpty(); diff --git a/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingStrategyFactory.java b/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingStrategyFactory.java index 2191c38017..e50d560d18 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingStrategyFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingStrategyFactory.java @@ -125,7 +125,7 @@ public class TbRuleEngineProcessingStrategyFactory { } log.debug("[{}] Going to reprocess {} messages", queueName, toReprocess.size()); if (log.isTraceEnabled()) { - toReprocess.forEach((id, msg) -> log.trace("Going to reprocess [{}]: {}", id, TbMsg.fromBytes(result.getQueueId(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY))); + toReprocess.forEach((id, msg) -> log.trace("Going to reprocess [{}]: {}", id, TbMsg.fromBytes(result.getQueueName(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY))); } if (pauseBetweenRetries > 0) { try { @@ -164,10 +164,10 @@ public class TbRuleEngineProcessingStrategyFactory { log.debug("[{}] Reprocessing skipped for {} failed and {} timeout messages", queueName, result.getFailedMap().size(), result.getPendingMap().size()); } if (log.isTraceEnabled()) { - result.getFailedMap().forEach((id, msg) -> log.trace("Failed messages [{}]: {}", id, TbMsg.fromBytes(result.getQueueId(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY))); + result.getFailedMap().forEach((id, msg) -> log.trace("Failed messages [{}]: {}", id, TbMsg.fromBytes(result.getQueueName(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY))); } if (log.isTraceEnabled()) { - result.getPendingMap().forEach((id, msg) -> log.trace("Timeout messages [{}]: {}", id, TbMsg.fromBytes(result.getQueueId(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY))); + result.getPendingMap().forEach((id, msg) -> log.trace("Timeout messages [{}]: {}", id, TbMsg.fromBytes(result.getQueueName(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY))); } return new TbRuleEngineProcessingDecision(true, null); } diff --git a/application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsInvokeService.java b/application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsInvokeService.java index 6aac90b281..d731ef1536 100644 --- a/application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsInvokeService.java +++ b/application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsInvokeService.java @@ -159,6 +159,8 @@ public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeSer } else { return ((Invocable) engine).invokeFunction(functionName, args); } + } catch (ScriptException e) { + throw new ExecutionException(e); } catch (Exception e) { onScriptExecutionError(scriptId, e, functionName); throw new ExecutionException(e); diff --git a/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java b/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java index 3bee5ca720..86b364afd4 100644 --- a/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java +++ b/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java @@ -57,6 +57,9 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService { @Value("${queue.js.max_requests_timeout}") private long maxRequestsTimeout; + @Value("${queue.js.max_exec_requests_timeout:2000}") + private long maxExecRequestsTimeout; + @Getter @Value("${js.remote.max_errors}") private int maxErrors; @@ -170,7 +173,7 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService { .setScriptIdMSB(scriptId.getMostSignificantBits()) .setScriptIdLSB(scriptId.getLeastSignificantBits()) .setFunctionName(functionName) - .setTimeout((int) maxRequestsTimeout) + .setTimeout((int) maxExecRequestsTimeout) .setScriptBody(scriptBody); for (Object arg : args) { @@ -197,7 +200,6 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService { @Override public void onFailure(Throwable t) { - onScriptExecutionError(scriptId, t, scriptBody); if (t instanceof TimeoutException || (t.getCause() != null && t.getCause() instanceof TimeoutException)) { queueTimeoutMsgs.incrementAndGet(); } @@ -212,8 +214,14 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService { return invokeResult.getResult(); } else { final RuntimeException e = new RuntimeException(invokeResult.getErrorDetails()); - onScriptExecutionError(scriptId, e, scriptBody); - log.debug("[{}] Failed to compile script due to [{}]: {}", scriptId, invokeResult.getErrorCode().name(), invokeResult.getErrorDetails()); + if (JsInvokeProtos.JsInvokeErrorCode.TIMEOUT_ERROR.equals(invokeResult.getErrorCode())) { + onScriptExecutionError(scriptId, e, scriptBody); + queueTimeoutMsgs.incrementAndGet(); + } else if (JsInvokeProtos.JsInvokeErrorCode.COMPILATION_ERROR.equals(invokeResult.getErrorCode())) { + onScriptExecutionError(scriptId, e, scriptBody); + } + queueFailedMsgs.incrementAndGet(); + log.debug("[{}] Failed to invoke function due to [{}]: {}", scriptId, invokeResult.getErrorCode().name(), invokeResult.getErrorDetails()); throw e; } }, callbackExecutor); diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java index 23ba6fd19e..ff57aec801 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java @@ -42,6 +42,7 @@ public class CustomerUserPermissions extends AbstractPermissions { put(Resource.WIDGET_TYPE, widgetsPermissionChecker); put(Resource.EDGE, customerEntityPermissionChecker); put(Resource.RPC, rpcPermissionChecker); + put(Resource.DEVICE_PROFILE, deviceProfilePermissionChecker); } private static final PermissionChecker customerAlarmPermissionChecker = new PermissionChecker() { @@ -152,4 +153,19 @@ public class CustomerUserPermissions extends AbstractPermissions { return user.getTenantId().equals(entity.getTenantId()); } }; + + private static final PermissionChecker deviceProfilePermissionChecker = new PermissionChecker.GenericPermissionChecker(Operation.READ) { + + @Override + @SuppressWarnings("unchecked") + public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { + if (!super.hasPermission(user, operation, entityId, entity)) { + return false; + } + if (entity.getTenantId() == null || entity.getTenantId().isNullUid()) { + return true; + } + return user.getTenantId().equals(entity.getTenantId()); + } + }; } 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 dae246ec49..4fdd6cf6ab 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 @@ -308,6 +308,10 @@ public abstract class BaseEntityImportService ID getInternalId(ID externalId, boolean throwExceptionIfNotFound) { if (externalId == null || externalId.isNullUid()) return null; + if (EntityType.TENANT.equals(externalId.getEntityType())) { + return (ID) ctx.getTenantId(); + } + EntityId localId = ctx.getInternalId(externalId); if (localId != null) { return (ID) localId; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/DeviceProfileImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/DeviceProfileImportService.java index 4c0b80b149..72be8e6bf7 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/DeviceProfileImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/DeviceProfileImportService.java @@ -53,7 +53,6 @@ public class DeviceProfileImportService extends BaseEntityImportService { cachePut(commit.getTxId(), new VersionCreationResult()); try { - List> gitFutures = new ArrayList<>(); + EntitiesExportCtx theCtx; switch (request.getType()) { case SINGLE_ENTITY: { - handleSingleEntityRequest(new SimpleEntitiesExportCtx(user, commit, (SingleEntityVersionCreateRequest) request)); + var ctx = new SimpleEntitiesExportCtx(user, commit, (SingleEntityVersionCreateRequest) request); + handleSingleEntityRequest(ctx); + theCtx = ctx; break; } case COMPLEX: { - handleComplexRequest(new ComplexEntitiesExportCtx(user, commit, (ComplexVersionCreateRequest) request)); + var ctx = new ComplexEntitiesExportCtx(user, commit, (ComplexVersionCreateRequest) request); + handleComplexRequest(ctx); + theCtx = ctx; break; } + default: + throw new RuntimeException("Unsupported request type: " + request.getType()); } - var resultFuture = Futures.transformAsync(Futures.allAsList(gitFutures), f -> gitServiceQueue.push(commit), executor); + var resultFuture = Futures.transformAsync(Futures.allAsList(theCtx.getFutures()), f -> gitServiceQueue.push(commit), executor); DonAsynchron.withCallback(resultFuture, result -> cachePut(commit.getTxId(), result), e -> processCommitError(user, request, commit, e), executor); } catch (Exception e) { processCommitError(user, request, commit, e); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitVersionControlQueueService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitVersionControlQueueService.java index 0c098d8281..413980d119 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitVersionControlQueueService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitVersionControlQueueService.java @@ -76,11 +76,13 @@ import org.thingsboard.server.service.sync.vc.data.VoidGitRequest; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.TreeMap; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; @@ -102,7 +104,7 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu private final SchedulerComponent scheduler; private final Map> pendingRequestMap = new HashMap<>(); - private final Map> chunkedMsgs = new ConcurrentHashMap<>(); + private final Map> chunkedMsgs = new ConcurrentHashMap<>(); @Value("${queue.vc.request-timeout:60000}") private int requestTimeout; @@ -286,7 +288,7 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu @SuppressWarnings("rawtypes") public ListenableFuture getEntity(TenantId tenantId, String versionId, EntityId entityId) { EntityContentGitRequest request = new EntityContentGitRequest(tenantId, versionId, entityId); - chunkedMsgs.put(request.getRequestId(), new LinkedHashMap<>()); + chunkedMsgs.put(request.getRequestId(), new HashMap<>()); registerAndSend(request, builder -> builder.setEntityContentRequest(EntityContentRequestMsg.newBuilder() .setVersionId(versionId) .setEntityType(entityId.getEntityType().name()) @@ -328,7 +330,7 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu @SuppressWarnings("rawtypes") public ListenableFuture> getEntities(TenantId tenantId, String versionId, EntityType entityType, int offset, int limit) { EntitiesContentGitRequest request = new EntitiesContentGitRequest(tenantId, versionId, entityType); - chunkedMsgs.put(request.getRequestId(), new LinkedHashMap<>()); + chunkedMsgs.put(request.getRequestId(), new HashMap<>()); registerAndSend(request, builder -> builder.setEntitiesContentRequest(EntitiesContentRequestMsg.newBuilder() .setVersionId(versionId) .setEntityType(entityType.name()) @@ -412,10 +414,10 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu ((ListVersionsGitRequest) request).getFuture().set(toPageData(listVersionsResponse)); } else if (vcResponseMsg.hasEntityContentResponse()) { TransportProtos.EntityContentResponseMsg responseMsg = vcResponseMsg.getEntityContentResponse(); - log.trace("[{}] received chunk {} for 'getEntity'", responseMsg.getChunkedMsgId(), responseMsg.getChunkIndex()); - var joined = joinChunks(requestId, responseMsg, 1); + log.trace("Received chunk {} for 'getEntity'", responseMsg.getChunkIndex()); + var joined = joinChunks(requestId, responseMsg, 0, 1); if (joined.isPresent()) { - log.trace("[{}] collected all chunks for 'getEntity'", responseMsg.getChunkedMsgId()); + log.trace("Collected all chunks for 'getEntity'"); ((EntityContentGitRequest) request).getFuture().set(joined.get().get(0)); } else { completed = false; @@ -424,7 +426,7 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu TransportProtos.EntitiesContentResponseMsg responseMsg = vcResponseMsg.getEntitiesContentResponse(); TransportProtos.EntityContentResponseMsg item = responseMsg.getItem(); if (responseMsg.getItemsCount() > 0) { - var joined = joinChunks(requestId, item, responseMsg.getItemsCount()); + var joined = joinChunks(requestId, item, responseMsg.getItemIdx(), responseMsg.getItemsCount()); if (joined.isPresent()) { ((EntitiesContentGitRequest) request).getFuture().set(joined.get()); } else { @@ -459,16 +461,17 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu } @SuppressWarnings("rawtypes") - private Optional> joinChunks(UUID requestId, TransportProtos.EntityContentResponseMsg responseMsg, int expectedMsgCount) { + private Optional> joinChunks(UUID requestId, TransportProtos.EntityContentResponseMsg responseMsg, int itemIdx, int expectedMsgCount) { var chunksMap = chunkedMsgs.get(requestId); if (chunksMap == null) { return Optional.empty(); } - String[] msgChunks = chunksMap.computeIfAbsent(responseMsg.getChunkedMsgId(), id -> new String[responseMsg.getChunksCount()]); + String[] msgChunks = chunksMap.computeIfAbsent(itemIdx, id -> new String[responseMsg.getChunksCount()]); msgChunks[responseMsg.getChunkIndex()] = responseMsg.getData(); if (chunksMap.size() == expectedMsgCount && chunksMap.values().stream() .allMatch(chunks -> CollectionsUtil.countNonNull(chunks) == chunks.length)) { - return Optional.of(chunksMap.values().stream() + return Optional.of(chunksMap.entrySet().stream() + .sorted(Comparator.comparingInt(Map.Entry::getKey)).map(Map.Entry::getValue) .map(chunks -> String.join("", chunks)) .map(this::toData) .collect(Collectors.toList())); diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/AttributeData.java b/application/src/main/java/org/thingsboard/server/service/telemetry/AttributeData.java index 9b67628aa3..5eb258c2a6 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/AttributeData.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/AttributeData.java @@ -32,17 +32,17 @@ public class AttributeData implements Comparable{ this.value = value; } - @ApiModelProperty(position = 1, value = "Timestamp last updated attribute, in milliseconds", example = "1609459200000", readOnly = true) + @ApiModelProperty(position = 1, value = "Timestamp last updated attribute, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public long getLastUpdateTs() { return lastUpdateTs; } - @ApiModelProperty(position = 2, value = "String representing attribute key", example = "active", readOnly = true) + @ApiModelProperty(position = 2, value = "String representing attribute key", example = "active", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public String getKey() { return key; } - @ApiModelProperty(position = 3, value = "Object representing value of attribute key", example = "false", readOnly = true) + @ApiModelProperty(position = 3, value = "Object representing value of attribute key", example = "false", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public Object getValue() { return value; } diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/TsData.java b/application/src/main/java/org/thingsboard/server/service/telemetry/TsData.java index e48561a965..56d1785d45 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/TsData.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/TsData.java @@ -30,12 +30,12 @@ public class TsData implements Comparable{ this.value = value; } - @ApiModelProperty(position = 1, value = "Timestamp last updated timeseries, in milliseconds", example = "1609459200000", readOnly = true) + @ApiModelProperty(position = 1, value = "Timestamp last updated timeseries, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public long getTs() { return ts; } - @ApiModelProperty(position = 2, value = "Object representing value of timeseries key", example = "20", readOnly = true) + @ApiModelProperty(position = 2, value = "Object representing value of timeseries key", example = "20", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public Object getValue() { return value; } diff --git a/application/src/main/resources/logback.xml b/application/src/main/resources/logback.xml index 4784ad2d43..941bd7d278 100644 --- a/application/src/main/resources/logback.xml +++ b/application/src/main/resources/logback.xml @@ -27,7 +27,7 @@ - + diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 9b458734f7..17b996934e 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -148,7 +148,7 @@ ui: # Help parameters help: # Base url for UI help assets - base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-3.3.4}" + base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-3.4}" database: ts_max_intervals: "${DATABASE_TS_MAX_INTERVALS:700}" # Max number of DB queries generated by single API call to fetch telemetry records @@ -326,7 +326,9 @@ actors: # Specify thread pool size for javascript executor service js_thread_pool_size: "${ACTORS_RULE_JS_THREAD_POOL_SIZE:50}" # Specify thread pool size for mail sender executor service - mail_thread_pool_size: "${ACTORS_RULE_MAIL_THREAD_POOL_SIZE:50}" + mail_thread_pool_size: "${ACTORS_RULE_MAIL_THREAD_POOL_SIZE:40}" + # Specify thread pool size for password reset emails + mail_password_reset_thread_pool_size: "${ACTORS_RULE_MAIL_PASSWORD_RESET_THREAD_POOL_SIZE:10}" # Specify thread pool size for sms sender executor service sms_thread_pool_size: "${ACTORS_RULE_SMS_THREAD_POOL_SIZE:50}" # Whether to allow usage of system mail service for rules @@ -545,10 +547,6 @@ spring: audit-log: # Enable/disable audit log functionality. enabled: "${AUDIT_LOG_ENABLED:true}" - # Specify partitioning size for audit log by tenant id storage. Example MINUTES, HOURS, DAYS, MONTHS - by_tenant_partitioning: "${AUDIT_LOG_BY_TENANT_PARTITIONING:MONTHS}" - # Number of days as history period if startTime and endTime are not specified - default_query_period: "${AUDIT_LOG_DEFAULT_QUERY_PERIOD:30}" # Logging levels per each entity type. # Allowed values: OFF (disable), W (log write operations), RW (log read and write operations) logging-level: @@ -945,10 +943,11 @@ queue: topic-properties: rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" - transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" + transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}" notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100;min.insync.replicas:1}" ota-updates: "${TB_QUEUE_KAFKA_OTA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}" + version-control: "${TB_QUEUE_KAFKA_VC_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}" consumer-stats: enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}" print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}" @@ -965,6 +964,8 @@ queue: transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" js-executor: "${TB_QUEUE_AWS_SQS_JE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" + ota-updates: "${TB_QUEUE_AWS_SQS_OTA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" + 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: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}" @@ -977,6 +978,7 @@ queue: transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" js-executor: "${TB_QUEUE_PUBSUB_JE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" + version-control: "${TB_QUEUE_PUBSUB_VC_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" service_bus: namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}" sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}" @@ -988,6 +990,7 @@ queue: transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" js-executor: "${TB_QUEUE_SERVICE_BUS_JE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" + version-control: "${TB_QUEUE_SERVICE_BUS_VC_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" rabbitmq: exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}" host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}" @@ -1004,6 +1007,7 @@ queue: transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" js-executor: "${TB_QUEUE_RABBIT_MQ_JE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" + 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 transport_api: @@ -1045,6 +1049,8 @@ queue: max_eval_requests_timeout: "${REMOTE_JS_MAX_EVAL_REQUEST_TIMEOUT:60000}" # JS max request timeout max_requests_timeout: "${REMOTE_JS_MAX_REQUEST_TIMEOUT:10000}" + # JS execution max request timeout + max_exec_requests_timeout: "${REMOTE_JS_MAX_EXEC_REQUEST_TIMEOUT:2000}" # JS response poll interval response_poll_interval: "${REMOTE_JS_RESPONSE_POLL_INTERVAL_MS:25}" rule-engine: diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java index a5af165699..d204750e32 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java @@ -20,9 +20,10 @@ import org.mockito.ArgumentMatcher; import org.mockito.Mockito; import org.springframework.boot.test.mock.mockito.SpyBean; import org.thingsboard.server.cluster.TbClusterService; -import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.HasName; +import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventType; @@ -37,7 +38,6 @@ import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; import org.thingsboard.server.dao.audit.AuditLogService; import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.service.gateway_device.GatewayNotificationsService; import java.util.ArrayList; import java.util.List; @@ -58,21 +58,19 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { @SpyBean protected AuditLogService auditLogService; - @SpyBean - protected GatewayNotificationsService gatewayNotificationsService; - protected final String msgErrorPermission = "You don't have permission to perform this operation!"; protected final String msgErrorShouldBeSpecified = "should be specified"; + protected final String msgErrorNotFound = "Requested item wasn't found!"; protected void testNotifyEntityAllOneTime(HasName entity, EntityId entityId, EntityId originatorId, TenantId tenantId, CustomerId customerId, UserId userId, String userName, ActionType actionType, Object... additionalInfo) { int cntTime = 1; - testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); + testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); ArgumentMatcher matcherOriginatorId = argument -> argument.equals(originatorId); - testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime); Mockito.reset(tbClusterService, auditLogService); } @@ -85,27 +83,35 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.eq(edgeTypeByActionType(actionType))); ArgumentMatcher matcherOriginatorId = argument -> argument.equals(relation.getTo()); ArgumentMatcher matcherEntityClassEquals = Objects::isNull; - testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, + ArgumentMatcher matcherCustomerId = customerId == null ? + argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId); + ArgumentMatcher matcherUserId = userId == null ? + argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId); + testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime, extractMatcherAdditionalInfo(additionalInfo)); testPushMsgToRuleEngineNever(relation.getTo()); matcherOriginatorId = argument -> argument.equals(relation.getFrom()); - testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, + testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime, extractMatcherAdditionalInfo(additionalInfo)); testPushMsgToRuleEngineNever(relation.getFrom()); Mockito.reset(tbClusterService, auditLogService); } protected void testNotifyEntityAllManyRelation(EntityRelation relation, - TenantId tenantId, CustomerId customerId, UserId userId, String userName, - ActionType actionType, int cntTime) { + TenantId tenantId, CustomerId customerId, UserId userId, String userName, + ActionType actionType, int cntTime) { Mockito.verify(tbClusterService, times(cntTime)).sendNotificationMsgToEdge(Mockito.eq(tenantId), Mockito.isNull(), Mockito.isNull(), Mockito.any(), Mockito.eq(EdgeEventType.RELATION), Mockito.eq(edgeTypeByActionType(actionType))); ArgumentMatcher matcherOriginatorId = argument -> argument.getClass().equals(relation.getFrom().getClass()); ArgumentMatcher matcherEntityClassEquals = Objects::isNull; - testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, + ArgumentMatcher matcherCustomerId = customerId == null ? + argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId); + ArgumentMatcher matcherUserId = userId == null ? + argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId); + testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime * 2, 1); - testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, new Tenant(), cntTime); Mockito.reset(tbClusterService, auditLogService); } @@ -113,16 +119,16 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { TenantId tenantId, CustomerId customerId, UserId userId, String userName, ActionType actionType, Object... additionalInfo) { int cntTime = 1; - testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); + testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); testLogEntityActionEntityEqClass(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); ArgumentMatcher matcherOriginatorId = argument -> argument.equals(originatorId); - testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime); Mockito.reset(tbClusterService, auditLogService); } protected void testNotifyEntityNeverMsgToEdgeServiceOneTime(HasName entity, EntityId entityId, TenantId tenantId, ActionType actionType) { - testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, 1); + testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, 1); testLogEntityActionNever(entityId, entity); testPushMsgToRuleEngineNever(entityId); Mockito.reset(tbClusterService, auditLogService); @@ -132,13 +138,13 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { TenantId tenantId, CustomerId customerId, UserId userId, String userName, ActionType actionType, Object... additionalInfo) { int cntTime = 1; - testNotificationMsgToEdgeServiceNever(entityId); + testNotificationMsgToEdgeServiceNeverWithActionType(entityId, actionType); testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); ArgumentMatcher matcherOriginatorId = argument -> argument.equals(originatorId); if (ActionType.RELATIONS_DELETED.equals(actionType)) { testPushMsgToRuleEngineNever(originatorId); } else { - testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime); } Mockito.reset(tbClusterService, auditLogService); } @@ -148,12 +154,16 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { ActionType actionType, int cntTime, Object... additionalInfo) { EntityId entityId = createEntityId_NULL_UUID(entity); EntityId originatorId = createEntityId_NULL_UUID(originator); - testNotificationMsgToEdgeServiceNever(entityId); + testNotificationMsgToEdgeServiceNeverWithActionType(entityId, actionType); ArgumentMatcher matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); - testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, + ArgumentMatcher matcherCustomerId = customerId == null ? + argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId); + ArgumentMatcher matcherUserId = userId == null ? + argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId); + testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime, extractMatcherAdditionalInfo(additionalInfo)); - testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime); Mockito.reset(tbClusterService, auditLogService); } @@ -165,10 +175,13 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { testSendNotificationMsgToEdgeServiceTimeEntityEqAny(tenantId, actionTypeEdge, cntTimeEdge); ArgumentMatcher matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); - testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, + ArgumentMatcher matcherCustomerId = customerId == null ? + argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId); + ArgumentMatcher matcherUserId = userId == null ? + argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId); + testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime, extractMatcherAdditionalInfoClass(additionalInfo)); - testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTimeRuleEngine); - Mockito.reset(tbClusterService, auditLogService); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTimeRuleEngine); } protected void testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(HasName entity, HasName originator, @@ -178,9 +191,13 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { testSendNotificationMsgToEdgeServiceTimeEntityEqAny(tenantId, actionTypeEdge, cntTimeEdge); ArgumentMatcher matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); - testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, + ArgumentMatcher matcherCustomerId = customerId == null ? + argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId); + ArgumentMatcher matcherUserId = userId == null ? + argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId); + testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime, cntAdditionalInfo); - testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTimeEdge); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTimeEdge); Mockito.reset(tbClusterService, auditLogService); } @@ -189,12 +206,16 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { ActionType actionType, int cntTime, int cntAdditionalInfo) { EntityId entityId = createEntityId_NULL_UUID(entity); EntityId originatorId = createEntityId_NULL_UUID(originator); - testNotificationMsgToEdgeServiceNever(entityId); + testNotificationMsgToEdgeServiceNeverWithActionType(entityId, actionType); ArgumentMatcher matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); - testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, + ArgumentMatcher matcherCustomerId = customerId == null ? + argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId); + ArgumentMatcher matcherUserId = userId == null ? + argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId); + testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime, cntAdditionalInfo); - testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime); Mockito.reset(tbClusterService, auditLogService); } @@ -202,10 +223,10 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { TenantId tenantId, CustomerId customerId, UserId userId, String userName, ActionType actionType, Object... additionalInfo) { int cntTime = 1; - testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); + testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); ArgumentMatcher matcherOriginatorId = argument -> argument.equals(originatorId); - testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime); testBroadcastEntityStateChangeEventTime(entityId, tenantId, cntTime); Mockito.reset(tbClusterService, auditLogService); } @@ -214,34 +235,40 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { TenantId tenantId, CustomerId customerId, UserId userId, String userName, ActionType actionType, Object... additionalInfo) { int cntTime = 1; - testNotificationMsgToEdgeServiceNever(entityId); + testNotificationMsgToEdgeServiceNeverWithActionType(entityId, actionType); testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); ArgumentMatcher matcherOriginatorId = argument -> argument.equals(originatorId); - testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime); testBroadcastEntityStateChangeEventTime(entityId, tenantId, cntTime); Mockito.reset(tbClusterService, auditLogService); } - protected void testNotifyEntityBroadcastEntityStateChangeEventManyMsgToEdgeServiceNever(HasName entity, HasName originator, - TenantId tenantId, CustomerId customerId, UserId userId, String userName, - ActionType actionType, int cntTime, int cntAdditionalInfo) { + protected void testNotifyEntityBroadcastEntityStateChangeEventMany(HasName entity, HasName originator, + TenantId tenantId, CustomerId customerId, + UserId userId, String userName, ActionType actionType, + ActionType actionTypeEdge, + int cntTime, int cntTimeEdge, int cntTimeRuleEngine, + int cntAdditionalInfo) { EntityId entityId = createEntityId_NULL_UUID(entity); EntityId originatorId = createEntityId_NULL_UUID(originator); - testNotificationMsgToEdgeServiceNever(entityId); + testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionTypeEdge, cntTimeEdge); ArgumentMatcher matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); - testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, + ArgumentMatcher matcherCustomerId = customerId == null ? + argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId); + ArgumentMatcher matcherUserId = userId == null ? + argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId); + testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime, cntAdditionalInfo); - testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTimeRuleEngine); testBroadcastEntityStateChangeEventTime(entityId, tenantId, cntTime); - Mockito.reset(tbClusterService, auditLogService); } protected void testNotifyEntityMsgToEdgePushMsgToCoreOneTime(HasName entity, EntityId entityId, EntityId originatorId, TenantId tenantId, CustomerId customerId, UserId userId, String userName, ActionType actionType, Object... additionalInfo) { int cntTime = 1; - testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); + testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); tesPushMsgToCoreTime(cntTime); Mockito.reset(tbClusterService, auditLogService); @@ -252,7 +279,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Object... additionalInfo) { CustomerId customer_NULL_UUID = (CustomerId) EntityIdFactory.getByTypeAndUuid(EntityType.CUSTOMER, ModelConstants.NULL_UUID); EntityId entity_originator_NULL_UUID = createEntityId_NULL_UUID(entity); - testNotificationMsgToEdgeServiceNever(entity_originator_NULL_UUID); + testNotificationMsgToEdgeServiceNeverWithActionType(entity_originator_NULL_UUID, actionType); ArgumentMatcher matcherEntityEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher matcherError = argument -> argument.getMessage().contains(exp.getMessage()) & argument.getClass().equals(exp.getClass()); @@ -266,7 +293,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Object... additionalInfo) { CustomerId customer_NULL_UUID = (CustomerId) EntityIdFactory.getByTypeAndUuid(EntityType.CUSTOMER, ModelConstants.NULL_UUID); EntityId entity_originator_NULL_UUID = createEntityId_NULL_UUID(entity); - testNotificationMsgToEdgeServiceNever(entity_originator_NULL_UUID); + testNotificationMsgToEdgeServiceNeverWithActionType(entity_originator_NULL_UUID, actionType); ArgumentMatcher matcherEntityIsNull = Objects::isNull; ArgumentMatcher matcherError = argument -> argument.getMessage().contains(exp.getMessage()) & argument.getClass().equals(exp.getClass()); @@ -283,20 +310,11 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.reset(tbClusterService, auditLogService); } - protected void testNotificationUpdateGatewayOneTime(Device device, Device oldDevice) { - Mockito.verify(gatewayNotificationsService, times(1)).onDeviceUpdated(Mockito.eq(device), Mockito.eq(oldDevice)); - } - - protected void testNotificationUpdateGatewayNever() { - Mockito.verify(gatewayNotificationsService, never()).onDeviceUpdated(Mockito.any(Device.class), Mockito.any(Device.class)); - } - - protected void testNotificationDeleteGatewayOneTime(Device device) { - Mockito.verify(gatewayNotificationsService, times(1)).onDeviceDeleted(device); - } - - protected void testNotificationDeleteGatewayNever() { - Mockito.verify(gatewayNotificationsService, never()).onDeviceDeleted(Mockito.any(Device.class)); + private void testNotificationMsgToEdgeServiceNeverWithActionType(EntityId entityId, ActionType actionType) { + EdgeEventActionType edgeEventActionType = ActionType.CREDENTIALS_UPDATED.equals(actionType) ? + EdgeEventActionType.CREDENTIALS_UPDATED : edgeTypeByActionType(actionType); + Mockito.verify(tbClusterService, never()).sendNotificationMsgToEdge(Mockito.any(), + Mockito.any(), Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any(), Mockito.eq(edgeEventActionType)); } private void testNotificationMsgToEdgeServiceNever(EntityId entityId) { @@ -317,16 +335,24 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any()); } - private void testPushMsgToRuleEngineTime(ArgumentMatcher matcherOriginatorId, TenantId tenantId, int cntTime) { + protected void testBroadcastEntityStateChangeEventNever(EntityId entityId) { + Mockito.verify(tbClusterService, never()).broadcastEntityStateChangeEvent(Mockito.any(), + Mockito.any(entityId.getClass()), Mockito.any(ComponentLifecycleEvent.class)); + } + + private void testPushMsgToRuleEngineTime(ArgumentMatcher matcherOriginatorId, TenantId tenantId, HasName entity, int cntTime) { + tenantId = tenantId.isNullUid() && ((HasTenantId) entity).getTenantId() != null ? ((HasTenantId) entity).getTenantId() : tenantId; Mockito.verify(tbClusterService, times(cntTime)).pushMsgToRuleEngine(Mockito.eq(tenantId), Mockito.argThat(matcherOriginatorId), Mockito.any(TbMsg.class), Mockito.isNull()); } - private void testSendNotificationMsgToEdgeServiceTime(EntityId entityId, TenantId tenantId, ActionType actionType, int cntTime) { + private void testNotificationMsgToEdgeServiceTime(EntityId entityId, TenantId tenantId, ActionType actionType, int cntTime) { EdgeEventActionType edgeEventActionType = ActionType.CREDENTIALS_UPDATED.equals(actionType) ? EdgeEventActionType.CREDENTIALS_UPDATED : edgeTypeByActionType(actionType); + ArgumentMatcher matcherEntityId = cntTime == 1 ? argument -> argument.equals(entityId) : + argument -> argument.getClass().equals(entityId.getClass()); Mockito.verify(tbClusterService, times(cntTime)).sendNotificationMsgToEdge(Mockito.eq(tenantId), - Mockito.any(), Mockito.eq(entityId), Mockito.any(), Mockito.isNull(), + Mockito.any(), Mockito.argThat(matcherEntityId), Mockito.any(), Mockito.isNull(), Mockito.eq(edgeEventActionType)); } @@ -336,8 +362,10 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.eq(edgeTypeByActionType(actionType))); } - private void testBroadcastEntityStateChangeEventTime(EntityId entityId, TenantId tenantId, int cntTime) { - Mockito.verify(tbClusterService, times(cntTime)).broadcastEntityStateChangeEvent(Mockito.eq(tenantId), + protected void testBroadcastEntityStateChangeEventTime(EntityId entityId, TenantId tenantId, int cntTime) { + ArgumentMatcher matcherTenantIdId = cntTime > 1 || tenantId == null ? argument -> argument.getClass().equals(TenantId.class) : + argument -> argument.equals(tenantId) ; + Mockito.verify(tbClusterService, times(cntTime)).broadcastEntityStateChangeEvent(Mockito.argThat(matcherTenantIdId), Mockito.any(entityId.getClass()), Mockito.any(ComponentLifecycleEvent.class)); } @@ -348,9 +376,13 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { private void testLogEntityAction(HasName entity, EntityId originatorId, TenantId tenantId, CustomerId customerId, UserId userId, String userName, ActionType actionType, int cntTime, Object... additionalInfo) { - ArgumentMatcher matcherEntityEquals = entity == null ? Objects::isNull : argument -> argument.equals(entity); + ArgumentMatcher matcherEntityEquals = entity == null ? Objects::isNull : argument -> argument.toString().equals(entity.toString()); ArgumentMatcher matcherOriginatorId = argument -> argument.equals(originatorId); - testLogEntityActionAdditionalInfo(matcherEntityEquals, matcherOriginatorId, tenantId, customerId, userId, userName, + ArgumentMatcher matcherCustomerId = customerId == null ? + argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId); + ArgumentMatcher matcherUserId = userId == null ? + argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId); + testLogEntityActionAdditionalInfo(matcherEntityEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime, extractMatcherAdditionalInfo(additionalInfo)); } @@ -359,19 +391,24 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { ActionType actionType, int cntTime, Object... additionalInfo) { ArgumentMatcher matcherEntityEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher matcherOriginatorId = argument -> argument.equals(originatorId); - testLogEntityActionAdditionalInfo(matcherEntityEquals, matcherOriginatorId, tenantId, customerId, userId, userName, + ArgumentMatcher matcherCustomerId = customerId == null ? + argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId); + ArgumentMatcher matcherUserId = userId == null ? + argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId); + testLogEntityActionAdditionalInfo(matcherEntityEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime, extractMatcherAdditionalInfo(additionalInfo)); } private void testLogEntityActionAdditionalInfo(ArgumentMatcher matcherEntity, ArgumentMatcher matcherOriginatorId, - TenantId tenantId, CustomerId customerId, UserId userId, String userName, - ActionType actionType, int cntTime, List> matcherAdditionalInfos) { + TenantId tenantId, ArgumentMatcher matcherCustomerId, + ArgumentMatcher matcherUserId, String userName, ActionType actionType, + int cntTime, List> matcherAdditionalInfos) { switch (matcherAdditionalInfos.size()) { case 1: Mockito.verify(auditLogService, times(cntTime)) .logEntityAction(Mockito.eq(tenantId), - Mockito.eq(customerId), - Mockito.eq(userId), + Mockito.argThat(matcherCustomerId), + Mockito.argThat(matcherUserId), Mockito.eq(userName), Mockito.argThat(matcherOriginatorId), Mockito.argThat(matcherEntity), @@ -382,8 +419,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { case 2: Mockito.verify(auditLogService, times(cntTime)) .logEntityAction(Mockito.eq(tenantId), - Mockito.eq(customerId), - Mockito.eq(userId), + Mockito.argThat(matcherCustomerId), + Mockito.argThat(matcherUserId), Mockito.eq(userName), Mockito.argThat(matcherOriginatorId), Mockito.argThat(matcherEntity), @@ -395,8 +432,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { case 3: Mockito.verify(auditLogService, times(cntTime)) .logEntityAction(Mockito.eq(tenantId), - Mockito.eq(customerId), - Mockito.eq(userId), + Mockito.argThat(matcherCustomerId), + Mockito.argThat(matcherUserId), Mockito.eq(userName), Mockito.argThat(matcherOriginatorId), Mockito.argThat(matcherEntity), @@ -409,8 +446,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { default: Mockito.verify(auditLogService, times(cntTime)) .logEntityAction(Mockito.eq(tenantId), - Mockito.eq(customerId), - Mockito.eq(userId), + Mockito.argThat(matcherCustomerId), + Mockito.argThat(matcherUserId), Mockito.eq(userName), Mockito.argThat(matcherOriginatorId), Mockito.argThat(matcherEntity), @@ -420,14 +457,15 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { } private void testLogEntityActionAdditionalInfoAny(ArgumentMatcher matcherEntity, ArgumentMatcher matcherOriginatorId, - TenantId tenantId, CustomerId customerId, UserId userId, String userName, + TenantId tenantId, ArgumentMatcher matcherCustomerId, + ArgumentMatcher matcherUserId, String userName, ActionType actionType, int cntTime, int cntAdditionalInfo) { switch (cntAdditionalInfo) { case 1: Mockito.verify(auditLogService, times(cntTime)) .logEntityAction(Mockito.eq(tenantId), - Mockito.eq(customerId), - Mockito.eq(userId), + Mockito.argThat(matcherCustomerId), + Mockito.argThat(matcherUserId), Mockito.eq(userName), Mockito.argThat(matcherOriginatorId), Mockito.argThat(matcherEntity), @@ -438,8 +476,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { case 2: Mockito.verify(auditLogService, times(cntTime)) .logEntityAction(Mockito.eq(tenantId), - Mockito.eq(customerId), - Mockito.eq(userId), + Mockito.argThat(matcherCustomerId), + Mockito.argThat(matcherUserId), Mockito.eq(userName), Mockito.argThat(matcherOriginatorId), Mockito.argThat(matcherEntity), @@ -451,8 +489,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { case 3: Mockito.verify(auditLogService, times(cntTime)) .logEntityAction(Mockito.eq(tenantId), - Mockito.eq(customerId), - Mockito.eq(userId), + Mockito.argThat(matcherCustomerId), + Mockito.argThat(matcherUserId), Mockito.eq(userName), Mockito.argThat(matcherOriginatorId), Mockito.argThat(matcherEntity), @@ -465,8 +503,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { default: Mockito.verify(auditLogService, times(cntTime)) .logEntityAction(Mockito.eq(tenantId), - Mockito.eq(customerId), - Mockito.eq(userId), + Mockito.argThat(matcherCustomerId), + Mockito.argThat(matcherUserId), Mockito.eq(userName), Mockito.argThat(matcherOriginatorId), Mockito.argThat(matcherEntity), @@ -479,12 +517,14 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { CustomerId customerId, UserId userId, String userName, ActionType actionType, int cntTime, ArgumentMatcher matcherError, List> matcherAdditionalInfos) { + ArgumentMatcher matcherUserId = userId == null ? argument -> argument.getClass().equals(UserId.class) : + argument -> argument.equals(userId); switch (matcherAdditionalInfos.size()) { case 1: Mockito.verify(auditLogService, times(cntTime)) .logEntityAction(Mockito.eq(tenantId), Mockito.eq(customerId), - Mockito.eq(userId), + Mockito.argThat(matcherUserId), Mockito.eq(userName), Mockito.eq(originatorId), Mockito.argThat(matcherEntity), @@ -496,7 +536,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.verify(auditLogService, times(cntTime)) .logEntityAction(Mockito.eq(tenantId), Mockito.eq(customerId), - Mockito.eq(userId), + Mockito.argThat(matcherUserId), Mockito.eq(userName), Mockito.eq(originatorId), Mockito.argThat(matcherEntity), @@ -508,7 +548,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.verify(auditLogService, times(cntTime)) .logEntityAction(Mockito.eq(tenantId), Mockito.eq(customerId), - Mockito.eq(userId), + Mockito.argThat(matcherUserId), Mockito.eq(userName), Mockito.eq(originatorId), Mockito.argThat(matcherEntity), @@ -522,7 +562,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.verify(auditLogService, times(cntTime)) .logEntityAction(Mockito.eq(tenantId), Mockito.eq(customerId), - Mockito.eq(userId), + Mockito.argThat(matcherUserId), Mockito.eq(userName), Mockito.eq(originatorId), Mockito.argThat(matcherEntity), @@ -558,7 +598,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { return result; } - private EntityId createEntityId_NULL_UUID(HasName entity) { + protected EntityId createEntityId_NULL_UUID(HasName entity) { return EntityIdFactory.getByTypeAndUuid(entityClassToEntityTypeName(entity), ModelConstants.NULL_UUID); } @@ -571,6 +611,12 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { } private String entityClassToEntityTypeName(HasName entity) { + String entityType = entityClassToString(entity); + return "SAVE_OTA_PACKAGE_INFO_REQUEST".equals(entityType) || "OTA_PACKAGE_INFO".equals(entityType)? + EntityType.OTA_PACKAGE.name().toUpperCase(Locale.ENGLISH) : entityType; + } + + private String entityClassToString(HasName entity) { String className = entity.getClass().toString() .substring(entity.getClass().toString().lastIndexOf(".") + 1); List str = className.chars() 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 329bad55a5..75d3a0aa97 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -285,7 +285,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { protected void loginDifferentTenant() throws Exception { if (savedDifferentTenant != null) { - login(savedDifferentTenant.getEmail(), TENANT_ADMIN_PASSWORD); + login(DIFFERENT_TENANT_ADMIN_EMAIL, DIFFERENT_TENANT_ADMIN_PASSWORD); } else { loginSysAdmin(); @@ -422,12 +422,6 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { } protected DeviceProfile createDeviceProfile(String name, DeviceProfileTransportConfiguration deviceProfileTransportConfiguration) { - return createDeviceProfile(name, deviceProfileTransportConfiguration, null); - } - - protected DeviceProfile createDeviceProfile(String name, - DeviceProfileTransportConfiguration deviceProfileTransportConfiguration, - QueueId defaultQueueId) { DeviceProfile deviceProfile = new DeviceProfile(); deviceProfile.setName(name); deviceProfile.setType(DeviceProfileType.DEFAULT); @@ -445,7 +439,6 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { deviceProfile.setProfileData(deviceProfileData); deviceProfile.setDefault(false); deviceProfile.setDefaultRuleChainId(null); - deviceProfile.setDefaultQueueId(defaultQueueId); return deviceProfile; } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAdminControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAdminControllerTest.java index b89133f29a..81ecce8300 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseAdminControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseAdminControllerTest.java @@ -18,14 +18,29 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.server.common.data.AdminSettings; +import org.thingsboard.server.service.mail.DefaultMailService; -import static org.hamcrest.Matchers.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; public abstract class BaseAdminControllerTest extends AbstractControllerTest { + @Autowired + MailService mailService; + + @Autowired + DefaultMailService defaultMailService; + @Test public void testFindAdminSettingsByKey() throws Exception { loginSysAdmin(); @@ -100,5 +115,28 @@ public abstract class BaseAdminControllerTest extends AbstractControllerTest { doPost("/api/admin/settings/testMail", adminSettings) .andExpect(status().isOk()); } - + + @Test + public void testSendTestMailTimeout() throws Exception { + loginSysAdmin(); + AdminSettings adminSettings = doGet("/api/admin/settings/mail", AdminSettings.class); + ObjectNode objectNode = JacksonUtil.fromString(adminSettings.getJsonValue().toString(), ObjectNode.class); + + objectNode.put("smtpHost", "mail.gandi.net"); + objectNode.put("timeout", 1_000); + objectNode.put("username", "username"); + objectNode.put("password", "password"); + + adminSettings.setJsonValue(objectNode); + + Mockito.doAnswer((invocations) -> { + var jsonConfig = (JsonNode) invocations.getArgument(0); + var email = (String) invocations.getArgument(1); + + defaultMailService.sendTestMail(jsonConfig, email); + return null; + }).when(mailService).sendTestMail(Mockito.any(), Mockito.anyString()); + doPost("/api/admin/settings/testMail", adminSettings).andExpect(status().is5xxServerError()); + Mockito.doNothing().when(mailService).sendTestMail(Mockito.any(), Mockito.any()); + } } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java index cfb9489f69..66fc1a5178 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java @@ -283,6 +283,7 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest doDelete("/api/tenant/" + savedTenant2.getId().getId().toString()) .andExpect(status().isForbidden()) .andExpect(statusReason(containsString(msgErrorPermission))); + testNotifyEntityNever(savedDashboard.getId(), savedDashboard); loginSysAdmin(); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java index dc5f9efa23..ee4d005233 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java @@ -27,10 +27,14 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; +import org.springframework.boot.test.mock.mockito.SpyBean; import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntitySubtype; +import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; @@ -49,6 +53,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.service.gateway_device.GatewayNotificationsService; import java.util.ArrayList; import java.util.List; @@ -56,12 +61,15 @@ import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.containsString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; +import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; public abstract class BaseDeviceControllerTest extends AbstractControllerTest { - static final TypeReference> PAGE_DATA_DEVICE_TYPE_REF = new TypeReference<>() { - }; + static final TypeReference> PAGE_DATA_DEVICE_TYPE_REF = new TypeReference<>() {}; ListeningExecutorService executor; @@ -71,6 +79,9 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { private Tenant savedTenant; private User tenantAdmin; + @SpyBean + private GatewayNotificationsService gatewayNotificationsService; + @Before public void beforeTest() throws Exception { executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass())); @@ -113,6 +124,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Device savedDevice = doPost("/api/device", device, Device.class); Device oldDevice = new Device(savedDevice); + testNotifyEntityOneTimeMsgToEdgeServiceNever(savedDevice, savedDevice.getId(), savedDevice.getId(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED); @@ -203,7 +215,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { String savedDeviceIdStr = savedDevice.getId().getId().toString(); doPost("/api/device", savedDevice) - .andExpect( status().isNotFound()) + .andExpect(status().isNotFound()) .andExpect(statusReason(containsString(msgErrorNoFound("Device", savedDeviceIdStr)))); testNotifyEntityNever(savedDevice.getId(), savedDevice); @@ -221,6 +233,66 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { deleteDifferentTenant(); } + @Test + public void testSaveDeviceWithProfileFromDifferentTenant() throws Exception { + loginDifferentTenant(); + DeviceProfile differentProfile = createDeviceProfile("Different profile"); + differentProfile = doPost("/api/deviceProfile", differentProfile, DeviceProfile.class); + + loginTenantAdmin(); + Device device = new Device(); + device.setName("My device"); + device.setDeviceProfileId(differentProfile.getId()); + doPost("/api/device", device).andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("Device can`t be referencing to device profile from different tenant!"))); + } + + @Test + public void testSaveDeviceWithFirmwareFromDifferentTenant() throws Exception { + loginDifferentTenant(); + DeviceProfile differentProfile = createDeviceProfile("Different profile"); + differentProfile = doPost("/api/deviceProfile", differentProfile, DeviceProfile.class); + SaveOtaPackageInfoRequest firmwareInfo = new SaveOtaPackageInfoRequest(); + firmwareInfo.setDeviceProfileId(differentProfile.getId()); + firmwareInfo.setType(FIRMWARE); + firmwareInfo.setTitle("title"); + firmwareInfo.setVersion("1.0"); + firmwareInfo.setUrl("test.url"); + firmwareInfo.setUsesUrl(true); + OtaPackageInfo savedFw = doPost("/api/otaPackage", firmwareInfo, OtaPackageInfo.class); + + loginTenantAdmin(); + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + device.setFirmwareId(savedFw.getId()); + doPost("/api/device", device).andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("Can't assign firmware from different tenant!"))); + } + + @Test + public void testSaveDeviceWithSoftwareFromDifferentTenant() throws Exception { + loginDifferentTenant(); + DeviceProfile differentProfile = createDeviceProfile("Different profile"); + differentProfile = doPost("/api/deviceProfile", differentProfile, DeviceProfile.class); + SaveOtaPackageInfoRequest softwareInfo = new SaveOtaPackageInfoRequest(); + softwareInfo.setDeviceProfileId(differentProfile.getId()); + softwareInfo.setType(SOFTWARE); + softwareInfo.setTitle("title"); + softwareInfo.setVersion("1.0"); + softwareInfo.setUrl("test.url"); + softwareInfo.setUsesUrl(true); + OtaPackageInfo savedSw = doPost("/api/otaPackage", softwareInfo, OtaPackageInfo.class); + + loginTenantAdmin(); + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + device.setSoftwareId(savedSw.getId()); + doPost("/api/device", device).andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("Can't assign software from different tenant!"))); + } + @Test public void testFindDeviceById() throws Exception { Device device = new Device(); @@ -248,8 +320,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { } testNotifyManyEntityManyTimeMsgToEdgeServiceNever(new Device(), new Device(), - savedTenant.getId(), - tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, cntEntity); testNotificationUpdateGatewayNever(); @@ -813,7 +884,9 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new Device(), new Device(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, ActionType.ASSIGNED_TO_CUSTOMER, cntEntity, cntEntity, cntEntity * 2); + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); testNotificationUpdateGatewayNever(); + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); List loadedDevices = new ArrayList<>(cntEntity); PageLink pageLink = new PageLink(23); @@ -828,8 +901,6 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { assertThat(devices).containsExactlyInAnyOrderElementsOf(loadedDevices); - Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); - deleteEntitiesAsync("/api/customer/device/", loadedDevices, executor).get(TIMEOUT, TimeUnit.SECONDS); testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(new Device(), new Device(), @@ -1116,4 +1187,20 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Assert.assertEquals(0, pageData.getData().size()); } + + protected void testNotificationUpdateGatewayOneTime(Device device, Device oldDevice) { + Mockito.verify(gatewayNotificationsService, times(1)).onDeviceUpdated(Mockito.eq(device), Mockito.eq(oldDevice)); + } + + protected void testNotificationUpdateGatewayNever() { + Mockito.verify(gatewayNotificationsService, never()).onDeviceUpdated(Mockito.any(Device.class), Mockito.any(Device.class)); + } + + protected void testNotificationDeleteGatewayOneTime(Device device) { + Mockito.verify(gatewayNotificationsService, times(1)).onDeviceDeleted(device); + } + + protected void testNotificationDeleteGatewayNever() { + Mockito.verify(gatewayNotificationsService, never()).onDeviceDeleted(Mockito.any(Device.class)); + } } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java index 80266563ef..4f782b15b3 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java @@ -28,12 +28,16 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfileInfo; import org.thingsboard.server.common.data.DeviceProfileProvisionType; import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; @@ -44,6 +48,7 @@ import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadCo import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.exception.DataValidationException; @@ -58,6 +63,8 @@ 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; +import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; +import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; public abstract class BaseDeviceProfileControllerTest extends AbstractControllerTest { @@ -170,6 +177,25 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController Assert.assertEquals(savedDeviceProfile.getId(), foundDeviceProfileInfo.getId()); Assert.assertEquals(savedDeviceProfile.getName(), foundDeviceProfileInfo.getName()); Assert.assertEquals(savedDeviceProfile.getType(), foundDeviceProfileInfo.getType()); + + Customer customer = new Customer(); + customer.setTitle("Customer"); + customer.setTenantId(savedTenant.getId()); + Customer savedCustomer = doPost("/api/customer", customer, Customer.class); + + User customerUser = new User(); + customerUser.setAuthority(Authority.CUSTOMER_USER); + customerUser.setTenantId(savedTenant.getId()); + customerUser.setCustomerId(savedCustomer.getId()); + customerUser.setEmail("customer2@thingsboard.org"); + + createUserAndLogin(customerUser, "customer"); + + foundDeviceProfileInfo = doGet("/api/deviceProfileInfo/" + savedDeviceProfile.getId().getId().toString(), DeviceProfileInfo.class); + Assert.assertNotNull(foundDeviceProfileInfo); + Assert.assertEquals(savedDeviceProfile.getId(), foundDeviceProfileInfo.getId()); + Assert.assertEquals(savedDeviceProfile.getName(), foundDeviceProfileInfo.getName()); + Assert.assertEquals(savedDeviceProfile.getType(), foundDeviceProfileInfo.getType()); } @Test @@ -325,6 +351,80 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController testNotifyEntityNever(savedDeviceProfile.getId(), savedDeviceProfile); } + @Test + public void testSaveDeviceProfileWithRuleChainFromDifferentTenant() throws Exception { + loginDifferentTenant(); + RuleChain ruleChain = new RuleChain(); + ruleChain.setName("Different rule chain"); + RuleChain savedRuleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class); + + loginTenantAdmin(); + + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); + deviceProfile.setDefaultRuleChainId(savedRuleChain.getId()); + doPost("/api/deviceProfile", deviceProfile).andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("Can't assign rule chain from different tenant!"))); + } + + @Test + public void testSaveDeviceProfileWithDashboardFromDifferentTenant() throws Exception { + loginDifferentTenant(); + Dashboard dashboard = new Dashboard(); + dashboard.setTitle("Different dashboard"); + Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class); + + loginTenantAdmin(); + + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); + deviceProfile.setDefaultDashboardId(savedDashboard.getId()); + doPost("/api/deviceProfile", deviceProfile).andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("Can't assign dashboard from different tenant!"))); + } + + @Test + public void testSaveDeviceProfileWithFirmwareFromDifferentTenant() throws Exception { + loginDifferentTenant(); + DeviceProfile differentProfile = createDeviceProfile("Different profile"); + differentProfile = doPost("/api/deviceProfile", differentProfile, DeviceProfile.class); + SaveOtaPackageInfoRequest firmwareInfo = new SaveOtaPackageInfoRequest(); + firmwareInfo.setDeviceProfileId(differentProfile.getId()); + firmwareInfo.setType(FIRMWARE); + firmwareInfo.setTitle("title"); + firmwareInfo.setVersion("1.0"); + firmwareInfo.setUrl("test.url"); + firmwareInfo.setUsesUrl(true); + OtaPackageInfo savedFw = doPost("/api/otaPackage", firmwareInfo, OtaPackageInfo.class); + + loginTenantAdmin(); + + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); + deviceProfile.setFirmwareId(savedFw.getId()); + doPost("/api/deviceProfile", deviceProfile).andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("Can't assign firmware from different tenant!"))); + } + + @Test + public void testSaveDeviceProfileWithSoftwareFromDifferentTenant() throws Exception { + loginDifferentTenant(); + DeviceProfile differentProfile = createDeviceProfile("Different profile"); + differentProfile = doPost("/api/deviceProfile", differentProfile, DeviceProfile.class); + SaveOtaPackageInfoRequest softwareInfo = new SaveOtaPackageInfoRequest(); + softwareInfo.setDeviceProfileId(differentProfile.getId()); + softwareInfo.setType(SOFTWARE); + softwareInfo.setTitle("title"); + softwareInfo.setVersion("1.0"); + softwareInfo.setUrl("test.url"); + softwareInfo.setUsesUrl(true); + OtaPackageInfo savedSw = doPost("/api/otaPackage", softwareInfo, OtaPackageInfo.class); + + loginTenantAdmin(); + + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); + deviceProfile.setSoftwareId(savedSw.getId()); + doPost("/api/deviceProfile", deviceProfile).andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("Can't assign software from different tenant!"))); + } + @Test public void testDeleteDeviceProfile() throws Exception { DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); @@ -367,6 +467,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new DeviceProfile(), new DeviceProfile(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, ActionType.ADDED, cntEntity, cntEntity, cntEntity); + Mockito.reset(tbClusterService, auditLogService); List loadedDeviceProfiles = new ArrayList<>(); pageLink = new PageLink(17); @@ -988,7 +1089,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString(errorMsg))); - testNotifyEntityEqualsOneTimeServiceNeverError(deviceProfile,savedTenant.getId(), + testNotifyEntityEqualsOneTimeServiceNeverError(deviceProfile, savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(errorMsg)); } @@ -1003,7 +1104,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString(errorMsg))); - testNotifyEntityEqualsOneTimeServiceNeverError(deviceProfile,savedTenant.getId(), + testNotifyEntityEqualsOneTimeServiceNeverError(deviceProfile, savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(errorMsg)); } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseEntityRelationControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseEntityRelationControllerTest.java index 08a157484f..2d28e32906 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseEntityRelationControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseEntityRelationControllerTest.java @@ -354,10 +354,9 @@ public abstract class BaseEntityRelationControllerTest extends AbstractControlle doDelete(url) .andExpect(status().isNotFound()) - .andExpect(statusReason(containsString("Requested item wasn't found!"))); + .andExpect(statusReason(containsString(msgErrorNotFound))); testNotifyEntityNever(mainDevice.getId(), null); - } @Test @@ -377,7 +376,7 @@ public abstract class BaseEntityRelationControllerTest extends AbstractControlle doDelete(url) .andExpect(status().isNotFound()) - .andExpect(statusReason(containsString("Requested item wasn't found!"))); + .andExpect(statusReason(containsString(msgErrorNotFound))); testNotifyEntityNever(mainDevice.getId(), null); } @@ -570,7 +569,6 @@ public abstract class BaseEntityRelationControllerTest extends AbstractControlle .andExpect(status().isNotFound()) .andExpect(statusReason(containsString(msgErrorNoFound("Device", relation.getFrom().getId().toString())))); - deleteDifferentTenant(); } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java index 615914c6d7..f1121dfb14 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java @@ -32,6 +32,7 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.servlet.ResultActions; import org.thingsboard.common.util.ThingsBoardExecutors; @@ -41,6 +42,7 @@ import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityViewInfo; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.objects.AttributesEntityView; @@ -52,6 +54,7 @@ import org.thingsboard.server.common.data.query.EntityKey; import org.thingsboard.server.common.data.query.EntityKeyType; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.model.ModelConstants; import java.util.ArrayList; @@ -92,7 +95,6 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes @Before public void beforeTest() throws Exception { - log.debug("beforeTest"); executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass())); loginTenantAdmin(); @@ -126,6 +128,9 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes @Test public void testSaveEntityView() throws Exception { String name = "Test entity view"; + + Mockito.reset(tbClusterService, auditLogService); + EntityView savedView = getNewSavedEntityView(name); Assert.assertNotNull(savedView); @@ -140,6 +145,12 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes assertEquals(savedView, foundEntityView); + testBroadcastEntityStateChangeEventTime(foundEntityView.getId(), tenantId, 1); + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(foundEntityView, foundEntityView, + tenantId, tenantAdminCustomerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, + ActionType.ADDED, ActionType.ADDED, 1, 0, 1); + Mockito.reset(tbClusterService, auditLogService); + savedView.setName("New test entity view"); doPost("/api/entityView", savedView, EntityView.class); @@ -147,23 +158,57 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes assertEquals(savedView, foundEntityView); - doGet("/api/tenant/entityViews?entityViewName=" + name, EntityView.class, status().isNotFound()); + testBroadcastEntityStateChangeEventTime(foundEntityView.getId(), tenantId, 1); + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(foundEntityView, foundEntityView, + tenantId, tenantAdminCustomerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, + ActionType.UPDATED, ActionType.UPDATED, 1, 1, 5); + + doGet("/api/tenant/entityViews?entityViewName=" + name) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNotFound))); } @Test public void testSaveEntityViewWithViolationOfValidation() throws Exception { EntityView entityView = createEntityView(RandomStringUtils.randomAlphabetic(300), 0, 0); - doPost("/api/entityView", entityView).andExpect(statusReason(containsString("length of name must be equal or less than 255"))); + + Mockito.reset(tbClusterService, auditLogService); + + String msgError = msgErrorFieldLength("name"); + doPost("/api/entityView", entityView) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); + + testNotifyEntityEqualsOneTimeServiceNeverError(entityView, + tenantId, tenantAdminUserId, TENANT_ADMIN_EMAIL, + ActionType.ADDED, new DataValidationException(msgError)); + Mockito.reset(tbClusterService, auditLogService); + entityView.setName("Normal name"); + msgError = msgErrorFieldLength("type"); entityView.setType(RandomStringUtils.randomAlphabetic(300)); - doPost("/api/entityView", entityView).andExpect(statusReason(containsString("length of type must be equal or less than 255"))); + doPost("/api/entityView", entityView) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); + + testNotifyEntityEqualsOneTimeServiceNeverError(entityView, + tenantId, tenantAdminUserId, TENANT_ADMIN_EMAIL, + ActionType.ADDED, new DataValidationException(msgError)); } @Test public void testUpdateEntityViewFromDifferentTenant() throws Exception { EntityView savedView = getNewSavedEntityView("Test entity view"); loginDifferentTenant(); - doPost("/api/entityView", savedView, EntityView.class, status().isForbidden()); + + Mockito.reset(tbClusterService, auditLogService); + + doPost("/api/entityView", savedView) + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); + + testNotifyEntityNever(savedView.getId(), savedView); + deleteDifferentTenant(); } @@ -174,20 +219,36 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes view.setCustomerId(customer.getId()); EntityView savedView = doPost("/api/entityView", view, EntityView.class); - doDelete("/api/entityView/" + savedView.getId().getId().toString()) + Mockito.reset(tbClusterService, auditLogService); + + String entityIdStr = savedView.getId().getId().toString(); + doDelete("/api/entityView/" + entityIdStr) .andExpect(status().isOk()); - doGet("/api/entityView/" + savedView.getId().getId().toString()) - .andExpect(status().isNotFound()); + testNotifyEntityBroadcastEntityStateChangeEventOneTimeMsgToEdgeServiceNever(savedView, savedView.getId(), savedView.getId(), + tenantId, view.getCustomerId(), tenantAdminUserId, TENANT_ADMIN_EMAIL, + ActionType.DELETED, entityIdStr); + + doGet("/api/entityView/" + entityIdStr) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Entity view",entityIdStr)))); } @Test public void testSaveEntityViewWithEmptyName() throws Exception { EntityView entityView = new EntityView(); entityView.setType("default"); + + Mockito.reset(tbClusterService, auditLogService); + + String msgError = "Entity view name " + msgErrorShouldBeSpecified; doPost("/api/entityView", entityView) .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Entity view name should be specified!"))); + .andExpect(statusReason(containsString(msgError))); + + testNotifyEntityEqualsOneTimeServiceNeverError(entityView, + tenantId, tenantAdminUserId, TENANT_ADMIN_EMAIL, + ActionType.ADDED, new DataValidationException(msgError)); } @Test @@ -195,8 +256,17 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes EntityView view = getNewSavedEntityView("Test entity view"); Customer savedCustomer = doPost("/api/customer", getNewCustomer("My customer"), Customer.class); view.setCustomerId(savedCustomer.getId()); + + Mockito.reset(tbClusterService, auditLogService); + EntityView savedView = doPost("/api/entityView", view, EntityView.class); + testBroadcastEntityStateChangeEventTime(savedView.getId(), tenantId, 1); + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(savedView, savedView, + tenantId, tenantAdminCustomerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, + ActionType.UPDATED, ActionType.UPDATED, 1, 1, 5); + Mockito.reset(tbClusterService, auditLogService); + EntityView assignedView = doPost( "/api/customer/" + savedCustomer.getId().getId().toString() + "/entityView/" + savedView.getId().getId().toString(), EntityView.class); @@ -205,18 +275,38 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes EntityView foundView = doGet("/api/entityView/" + savedView.getId().getId().toString(), EntityView.class); assertEquals(savedCustomer.getId(), foundView.getCustomerId()); + testBroadcastEntityStateChangeEventNever(foundView.getId()); + testNotifyEntityAllOneTime(foundView, foundView.getId(), foundView.getId(), + tenantId, foundView.getCustomerId(), tenantAdminUserId, TENANT_ADMIN_EMAIL, + ActionType.ASSIGNED_TO_CUSTOMER, + foundView.getId().getId().toString(), foundView.getCustomerId().getId().toString(), savedCustomer.getTitle()); + EntityView unAssignedView = doDelete("/api/customer/entityView/" + savedView.getId().getId().toString(), EntityView.class); assertEquals(ModelConstants.NULL_UUID, unAssignedView.getCustomerId().getId()); foundView = doGet("/api/entityView/" + savedView.getId().getId().toString(), EntityView.class); assertEquals(ModelConstants.NULL_UUID, foundView.getCustomerId().getId()); + + testBroadcastEntityStateChangeEventNever(foundView.getId()); + testNotifyEntityAllOneTime(unAssignedView, savedView.getId(), savedView.getId(), + tenantId, savedView.getCustomerId(), tenantAdminUserId, TENANT_ADMIN_EMAIL, + ActionType.UNASSIGNED_FROM_CUSTOMER, + savedView.getCustomerId().getId().toString(), savedCustomer.getTitle()); } @Test public void testAssignEntityViewToNonExistentCustomer() throws Exception { EntityView savedView = getNewSavedEntityView("Test entity view"); - doPost("/api/customer/" + Uuids.timeBased().toString() + "/device/" + savedView.getId().getId().toString()) - .andExpect(status().isNotFound()); + + Mockito.reset(tbClusterService, auditLogService); + + String customerIdStr = Uuids.timeBased().toString(); + String msgError = msgErrorNoFound("Customer", customerIdStr); + doPost("/api/customer/" + customerIdStr + "/device/" + savedView.getId().getId().toString()) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgError))); + + testNotifyEntityNever(savedView.getId(), savedView); } @Test @@ -242,8 +332,13 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes EntityView savedView = getNewSavedEntityView("Test entity view"); + Mockito.reset(tbClusterService, auditLogService); + doPost("/api/customer/" + savedCustomer.getId().getId().toString() + "/entityView/" + savedView.getId().getId().toString()) - .andExpect(status().isForbidden()); + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); + + testNotifyEntityNever(savedView.getId(), savedView); loginSysAdmin(); @@ -257,8 +352,11 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes CustomerId customerId = customer.getId(); String urlTemplate = "/api/customer/" + customerId.getId().toString() + "/entityViewInfos?"; - List> viewFutures = new ArrayList<>(128); - for (int i = 0; i < 128; i++) { + Mockito.reset(tbClusterService, auditLogService); + + int cntEntity = 128; + List> viewFutures = new ArrayList<>(cntEntity); + for (int i = 0; i < cntEntity; i++) { String entityName = "Test entity view " + i; viewFutures.add(executor.submit(() -> new EntityViewInfo(doPost("/api/customer/" + customerId.getId().toString() + "/entityView/" @@ -269,6 +367,15 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes List loadedViews = loadListOfInfo(new PageLink(23), urlTemplate); assertThat(entityViewInfos).containsExactlyInAnyOrderElementsOf(loadedViews); + + testNotifyEntityBroadcastEntityStateChangeEventMany(new EntityView(), new EntityView(), + tenantId, tenantAdminCustomerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, + ActionType.ADDED, ActionType.ADDED, cntEntity, 0, cntEntity*2, 0); + + testNotifyEntityBroadcastEntityStateChangeEventMany(new EntityView(), new EntityView(), + tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, + ActionType.ASSIGNED_TO_CUSTOMER, ActionType.ASSIGNED_TO_CUSTOMER, cntEntity, cntEntity, + cntEntity*2, 3); } @Test @@ -289,12 +396,21 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes assertThat(namesOfView2).as(name2).containsExactlyInAnyOrderElementsOf(loadedNamesOfView2); deleteFutures.clear(); + + Mockito.reset(tbClusterService, auditLogService); + + int cntEntity = loadedNamesOfView1.size(); for (EntityView view : loadedNamesOfView1) { deleteFutures.add(executor.submit(() -> doDelete("/api/customer/entityView/" + view.getId().getId().toString()).andExpect(status().isOk()))); } Futures.allAsList(deleteFutures).get(TIMEOUT, SECONDS); + testBroadcastEntityStateChangeEventNever(loadedNamesOfView1.get(0).getId()); + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(new EntityView(), new EntityView(), + tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, + ActionType.UNASSIGNED_FROM_CUSTOMER, ActionType.UNASSIGNED_FROM_CUSTOMER, cntEntity, cntEntity, 2); + PageData pageData = doGetTypedWithPageLink(urlTemplate, PAGE_DATA_ENTITY_VIEW_TYPE_REF, new PageLink(4, 0, name1)); Assert.assertFalse(pageData.hasNext()); @@ -370,10 +486,8 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes Set expectedActualAttributesSet = Set.of("caKey1", "caKey2", "caKey3", "caKey4"); Set actualAttributesSet = putAttributesAndWait("{\"caKey1\":\"value1\", \"caKey2\":true, \"caKey3\":42.0, \"caKey4\":73}", expectedActualAttributesSet); - log.debug("got correct actualAttributesSet, saving new entity view..."); EntityView savedView = getNewSavedEntityView("Test entity view"); - log.debug("fetching entity view telemetry..."); List> values = await("telemetry/ENTITY_VIEW") .atMost(TIMEOUT, SECONDS) .until(() -> doGetAsyncTyped("/api/plugins/telemetry/ENTITY_VIEW/" + savedView.getId().getId().toString() + @@ -381,7 +495,6 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes }), x -> x.size() >= expectedActualAttributesSet.size()); - log.debug("asserting..."); assertEquals("value1", getValue(values, "caKey1")); assertEquals(true, getValue(values, "caKey2")); assertEquals(42.0, getValue(values, "caKey3")); @@ -526,7 +639,6 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes getWsClient().subscribeLatestUpdate(keysToSubscribe, dtf); String viewDeviceId = testDevice.getId().getId().toString(); - log.debug("deviceid {}", viewDeviceId); DeviceCredentials deviceCredentials = doGet("/api/device/" + viewDeviceId + "/credentials", DeviceCredentials.class); assertEquals(testDevice.getId(), deviceCredentials.getDeviceId()); @@ -534,7 +646,6 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes String accessToken = deviceCredentials.getCredentialsId(); assertNotNull(accessToken); - log.debug("creating mqtt client..."); String clientId = MqttAsyncClient.generateClientId(); MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", clientId, new MemoryPersistence()); @@ -542,14 +653,11 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes options.setUserName(accessToken); client.connect(options); awaitConnected(client, SECONDS.toMillis(30)); - log.debug("mqtt connected..."); MqttMessage message = new MqttMessage(); message.setPayload((stringKV).getBytes()); getWsClient().registerWaitForUpdate(); IMqttDeliveryToken token = client.publish("v1/devices/me/attributes", message); - log.debug("publish token.message {}", token.getMessage()); await("mqtt ack").pollInterval(5, MILLISECONDS).atMost(TIMEOUT, SECONDS).until(() -> token.getMessage() == null); - log.debug("token.message delivered {}", token.getMessage()); assertThat(getWsClient().waitForUpdate()).as("ws update received").isNotBlank(); return getAttributeKeys("DEVICE", viewDeviceId); } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseOtaPackageControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseOtaPackageControllerTest.java index cbc00b0987..18d76d96e8 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseOtaPackageControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseOtaPackageControllerTest.java @@ -21,6 +21,7 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; import org.springframework.mock.web.MockMultipartFile; import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; @@ -31,10 +32,12 @@ import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.dao.exception.DataValidationException; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -102,6 +105,8 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes firmwareInfo.setVersion(VERSION); firmwareInfo.setUsesUrl(false); + Mockito.reset(tbClusterService, auditLogService); + OtaPackageInfo savedFirmwareInfo = save(firmwareInfo); Assert.assertNotNull(savedFirmwareInfo); @@ -111,12 +116,20 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes Assert.assertEquals(firmwareInfo.getTitle(), savedFirmwareInfo.getTitle()); Assert.assertEquals(firmwareInfo.getVersion(), savedFirmwareInfo.getVersion()); + testNotifyEntityOneTimeMsgToEdgeServiceNever(savedFirmwareInfo, savedFirmwareInfo.getId(), savedFirmwareInfo.getId(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ADDED); + savedFirmwareInfo.setAdditionalInfo(JacksonUtil.newObjectNode()); save(new SaveOtaPackageInfoRequest(savedFirmwareInfo, false)); OtaPackageInfo foundFirmwareInfo = doGet("/api/otaPackage/info/" + savedFirmwareInfo.getId().getId().toString(), OtaPackageInfo.class); Assert.assertEquals(foundFirmwareInfo.getTitle(), savedFirmwareInfo.getTitle()); + + testNotifyEntityOneTimeMsgToEdgeServiceNever(foundFirmwareInfo, foundFirmwareInfo.getId(), foundFirmwareInfo.getId(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.UPDATED); } @Test @@ -127,14 +140,42 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes firmwareInfo.setTitle(RandomStringUtils.randomAlphabetic(300)); firmwareInfo.setVersion(VERSION); firmwareInfo.setUsesUrl(false); - doPost("/api/otaPackage", firmwareInfo).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); + String msgError = msgErrorFieldLength("title"); + + Mockito.reset(tbClusterService, auditLogService); + + doPost("/api/otaPackage", firmwareInfo) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); + + firmwareInfo.setTenantId(savedTenant.getId()); + testNotifyEntityEqualsOneTimeServiceNeverError(firmwareInfo, + savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ADDED, new DataValidationException(msgError)); + firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(RandomStringUtils.randomAlphabetic(300)); - doPost("/api/otaPackage", firmwareInfo).andExpect(statusReason(containsString("length of version must be equal or less than 255"))); + msgError = msgErrorFieldLength("version"); + doPost("/api/otaPackage", firmwareInfo) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); + + firmwareInfo.setTenantId(savedTenant.getId()); + testNotifyEntityEqualsOneTimeServiceNeverError(firmwareInfo, + savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ADDED, new DataValidationException(msgError)); + firmwareInfo.setVersion(VERSION); firmwareInfo.setUsesUrl(true); + msgError = msgErrorFieldLength("url"); firmwareInfo.setUrl(RandomStringUtils.randomAlphabetic(300)); - doPost("/api/otaPackage", firmwareInfo).andExpect(statusReason(containsString("length of url must be equal or less than 255"))); + doPost("/api/otaPackage", firmwareInfo) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); + + firmwareInfo.setTenantId(savedTenant.getId()); + testNotifyEntityEqualsOneTimeServiceNeverError(firmwareInfo, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); } @Test @@ -164,12 +205,19 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array()); - OtaPackageInfo savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); + Mockito.reset(tbClusterService, auditLogService); + + OtaPackage savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); Assert.assertEquals(FILE_NAME, savedFirmware.getFileName()); Assert.assertEquals(CONTENT_TYPE, savedFirmware.getContentType()); Assert.assertEquals(CHECKSUM_ALGORITHM, savedFirmware.getChecksumAlgorithm().name()); Assert.assertEquals(CHECKSUM, savedFirmware.getChecksum()); + + testNotifyEntityAllOneTime(savedFirmware, savedFirmware.getId(), savedFirmware.getId(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.UPDATED); } @Test @@ -184,10 +232,16 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes OtaPackageInfo savedFirmwareInfo = save(firmwareInfo); loginDifferentTenant(); + + Mockito.reset(tbClusterService, auditLogService); + doPost("/api/otaPackage", - new SaveOtaPackageInfoRequest(savedFirmwareInfo, false), - OtaPackageInfo.class, - status().isForbidden()); + new SaveOtaPackageInfoRequest(savedFirmwareInfo, false)) + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); + + testNotifyEntityNever(savedFirmwareInfo.getId(), savedFirmwareInfo); + deleteDifferentTenant(); } @@ -220,11 +274,12 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array()); - OtaPackageInfo savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); + OtaPackageInfo savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); OtaPackage foundFirmware = doGet("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString(), OtaPackage.class); Assert.assertNotNull(foundFirmware); - Assert.assertEquals(savedFirmware, new OtaPackageInfo(foundFirmware)); + Assert.assertEquals(savedFirmware, foundFirmware); Assert.assertEquals(DATA, foundFirmware.getData()); } @@ -239,17 +294,29 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes OtaPackageInfo savedFirmwareInfo = save(firmwareInfo); + Mockito.reset(tbClusterService, auditLogService); + doDelete("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString()) .andExpect(status().isOk()); + testNotifyEntityAllOneTime(savedFirmwareInfo, savedFirmwareInfo.getId(), savedFirmwareInfo.getId(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.DELETED, savedFirmwareInfo.getId().getId().toString()); + doGet("/api/otaPackage/info/" + savedFirmwareInfo.getId().getId().toString()) - .andExpect(status().isNotFound()); + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNotFound))); } @Test public void testFindTenantFirmwares() throws Exception { + + Mockito.reset(tbClusterService, auditLogService); + List otaPackages = new ArrayList<>(); - for (int i = 0; i < 165; i++) { + int cntEntity = 165; + int startIndexSaveData = 101; + for (int i = 0; i < cntEntity; i++) { SaveOtaPackageInfoRequest firmwareInfo = new SaveOtaPackageInfoRequest(); firmwareInfo.setDeviceProfileId(deviceProfileId); firmwareInfo.setType(FIRMWARE); @@ -259,16 +326,19 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes OtaPackageInfo savedFirmwareInfo = save(firmwareInfo); - if (i > 100) { + if (i >= startIndexSaveData) { MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array()); - OtaPackageInfo savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); - otaPackages.add(savedFirmware); - } else { - otaPackages.add(savedFirmwareInfo); + OtaPackage savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); + savedFirmwareInfo = new OtaPackageInfo(savedFirmware); } + otaPackages.add(savedFirmwareInfo); } + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new OtaPackageInfo(), new OtaPackageInfo(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ADDED, ActionType.ADDED, cntEntity, 0, (cntEntity*2 - startIndexSaveData)); + List loadedFirmwares = new ArrayList<>(); PageLink pageLink = new PageLink(24); PageData pageData; @@ -306,7 +376,7 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes if (i > 100) { MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array()); - OtaPackageInfo savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); + OtaPackage savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); savedFirmwareInfo = new OtaPackageInfo(savedFirmware); otaPackagesWithData.add(savedFirmwareInfo); } @@ -352,11 +422,11 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes return doPost("/api/otaPackage", firmwareInfo, OtaPackageInfo.class); } - protected OtaPackageInfo savaData(String urlTemplate, MockMultipartFile content, String... params) throws Exception { + protected OtaPackage savaData(String urlTemplate, MockMultipartFile content, String... params) throws Exception { MockMultipartHttpServletRequestBuilder postRequest = MockMvcRequestBuilders.multipart(urlTemplate, params); postRequest.file(content); setJwtToken(postRequest); - return readResponse(mockMvc.perform(postRequest).andExpect(status().isOk()), OtaPackageInfo.class); + return readResponse(mockMvc.perform(postRequest).andExpect(status().isOk()), OtaPackage.class); } } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseRuleChainControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseRuleChainControllerTest.java index 9395f99366..33602bbfa9 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseRuleChainControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseRuleChainControllerTest.java @@ -21,14 +21,17 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.dao.exception.DataValidationException; import java.util.ArrayList; import java.util.Collections; @@ -75,22 +78,45 @@ public abstract class BaseRuleChainControllerTest extends AbstractControllerTest public void testSaveRuleChain() throws Exception { RuleChain ruleChain = new RuleChain(); ruleChain.setName("RuleChain"); + + Mockito.reset(tbClusterService, auditLogService); + RuleChain savedRuleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class); Assert.assertNotNull(savedRuleChain); Assert.assertNotNull(savedRuleChain.getId()); Assert.assertTrue(savedRuleChain.getCreatedTime() > 0); Assert.assertEquals(ruleChain.getName(), savedRuleChain.getName()); + + testNotifyEntityOneTimeMsgToEdgeServiceNever(savedRuleChain, savedRuleChain.getId(), savedRuleChain.getId(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ADDED); + savedRuleChain.setName("New RuleChain"); doPost("/api/ruleChain", savedRuleChain, RuleChain.class); RuleChain foundRuleChain = doGet("/api/ruleChain/" + savedRuleChain.getId().getId().toString(), RuleChain.class); Assert.assertEquals(savedRuleChain.getName(), foundRuleChain.getName()); + + testNotifyEntityOneTimeMsgToEdgeServiceNever(savedRuleChain, savedRuleChain.getId(), savedRuleChain.getId(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.UPDATED); } @Test public void testSaveRuleChainWithViolationOfLengthValidation() throws Exception { + + Mockito.reset(tbClusterService, auditLogService); + RuleChain ruleChain = new RuleChain(); ruleChain.setName(RandomStringUtils.randomAlphabetic(300)); - doPost("/api/ruleChain", ruleChain).andExpect(statusReason(containsString("length of name must be equal or less than 255"))); + String msgError = msgErrorFieldLength("name"); + doPost("/api/ruleChain", ruleChain) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); + + ruleChain.setTenantId(savedTenant.getId()); + testNotifyEntityEqualsOneTimeServiceNeverError(ruleChain, + savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ADDED, new DataValidationException(msgError)); } @Test @@ -109,11 +135,19 @@ public abstract class BaseRuleChainControllerTest extends AbstractControllerTest ruleChain.setName("RuleChain"); RuleChain savedRuleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class); + Mockito.reset(tbClusterService, auditLogService); + + String entityIdStr = savedRuleChain.getId().getId().toString(); doDelete("/api/ruleChain/" + savedRuleChain.getId().getId().toString()) .andExpect(status().isOk()); - doGet("/api/ruleChain/" + savedRuleChain.getId().getId().toString()) - .andExpect(status().isNotFound()); + testNotifyEntityBroadcastEntityStateChangeEventOneTimeMsgToEdgeServiceNever(savedRuleChain, savedRuleChain.getId(), savedRuleChain.getId(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.DELETED, savedRuleChain.getId().getId().toString()); + + doGet("/api/ruleChain/" + entityIdStr) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Rule chain", entityIdStr)))); } @Test @@ -121,15 +155,20 @@ public abstract class BaseRuleChainControllerTest extends AbstractControllerTest Edge edge = constructEdge("My edge", "default"); Edge savedEdge = doPost("/api/edge", edge, Edge.class); + List edgeRuleChains = new ArrayList<>(); PageLink pageLink = new PageLink(17); PageData pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/ruleChains?", - new TypeReference<>() {}, pageLink); + new TypeReference<>() { + }, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(1, pageData.getTotalElements()); edgeRuleChains.addAll(pageData.getData()); - for (int i = 0; i < 28; i++) { + Mockito.reset(tbClusterService, auditLogService); + + int cntEntity = 28; + for (int i = 0; i < cntEntity; i++) { RuleChain ruleChain = new RuleChain(); ruleChain.setName("RuleChain " + i); ruleChain.setType(RuleChainType.EDGE); @@ -139,11 +178,21 @@ public abstract class BaseRuleChainControllerTest extends AbstractControllerTest edgeRuleChains.add(savedRuleChain); } + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new RuleChain(), new RuleChain(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ADDED, ActionType.ADDED, cntEntity, 0, cntEntity * 2); + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new RuleChain(), new RuleChain(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ASSIGNED_TO_EDGE, ActionType.ASSIGNED_TO_EDGE, cntEntity, cntEntity, cntEntity * 2, + new String(), new String(), new String()); + Mockito.reset(tbClusterService, auditLogService); + List loadedEdgeRuleChains = new ArrayList<>(); pageLink = new PageLink(17); do { pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/ruleChains?", - new TypeReference<>() {}, pageLink); + new TypeReference<>() { + }, pageLink); loadedEdgeRuleChains.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -162,9 +211,14 @@ public abstract class BaseRuleChainControllerTest extends AbstractControllerTest } } + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(new RuleChain(), new RuleChain(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.UNASSIGNED_FROM_EDGE, ActionType.UNASSIGNED_FROM_EDGE, cntEntity, cntEntity, 3); + pageLink = new PageLink(17); pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/ruleChains?", - new TypeReference<>() {}, pageLink); + new TypeReference<>() { + }, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(1, pageData.getTotalElements()); } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseTbResourceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseTbResourceControllerTest.java index 4088e78563..d3153f57bc 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseTbResourceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseTbResourceControllerTest.java @@ -21,14 +21,17 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.dao.exception.DataValidationException; import java.util.ArrayList; import java.util.Collections; @@ -75,6 +78,9 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes @Test public void testSaveTbResource() throws Exception { + + Mockito.reset(tbClusterService, auditLogService); + TbResource resource = new TbResource(); resource.setResourceType(ResourceType.JKS); resource.setTitle("My first resource"); @@ -83,6 +89,10 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes TbResource savedResource = save(resource); + testNotifyEntityOneTimeMsgToEdgeServiceNever(savedResource, savedResource.getId(), savedResource.getId(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ADDED); + Assert.assertNotNull(savedResource); Assert.assertNotNull(savedResource.getId()); Assert.assertTrue(savedResource.getCreatedTime() > 0); @@ -98,6 +108,10 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes TbResource foundResource = doGet("/api/resource/" + savedResource.getId().getId().toString(), TbResource.class); Assert.assertEquals(foundResource.getTitle(), savedResource.getTitle()); + + testNotifyEntityOneTimeMsgToEdgeServiceNever(foundResource, foundResource.getId(), foundResource.getId(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.UPDATED); } @Test @@ -107,7 +121,16 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes resource.setTitle(RandomStringUtils.randomAlphabetic(300)); resource.setFileName(DEFAULT_FILE_NAME); resource.setData("Test Data"); - doPost("/api/resource", resource).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); + + Mockito.reset(tbClusterService, auditLogService); + + String msgError = msgErrorFieldLength("title"); + doPost("/api/resource", resource) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); + + testNotifyEntityEqualsOneTimeServiceNeverError(resource, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); } @Test @@ -118,10 +141,24 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes resource.setFileName(DEFAULT_FILE_NAME); resource.setData("Test Data"); - TbResource savedResource = save(resource); + TbResource savedResource = save(resource); loginDifferentTenant(); - doPostWithTypedResponse("/api/resource", savedResource, new TypeReference<>(){}, status().isForbidden()); + + Mockito.reset(tbClusterService, auditLogService); + + doPost("/api/resource", savedResource) + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); + + testNotifyEntityNever(savedResource.getId(), savedResource); + + doDelete("/api/resource/" + savedResource.getId().getId().toString()) + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); + + testNotifyEntityNever(savedResource.getId(), savedResource); + deleteDifferentTenant(); } @@ -150,17 +187,29 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes TbResource savedResource = save(resource); - doDelete("/api/resource/" + savedResource.getId().getId().toString()) + Mockito.reset(tbClusterService, auditLogService); + String resourceIdStr = savedResource.getId().getId().toString(); + doDelete("/api/resource/" + resourceIdStr) .andExpect(status().isOk()); + + testNotifyEntityOneTimeMsgToEdgeServiceNever(savedResource, savedResource.getId(), savedResource.getId(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.DELETED, resourceIdStr); + doGet("/api/resource/" + savedResource.getId().getId().toString()) - .andExpect(status().isNotFound()); + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Resource", resourceIdStr)))); } @Test public void testFindTenantTbResources() throws Exception { + + Mockito.reset(tbClusterService, auditLogService); + List resources = new ArrayList<>(); - for (int i = 0; i < 173; i++) { + int cntEntity = 173; + for (int i = 0; i < cntEntity; i++) { TbResource resource = new TbResource(); resource.setTitle("Resource" + i); resource.setResourceType(ResourceType.JKS); @@ -173,7 +222,7 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes PageData pageData; do { pageData = doGetTypedWithPageLink("/api/resource?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); loadedResources.addAll(pageData.getData()); if (pageData.hasNext()) { @@ -181,6 +230,10 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes } } while (pageData.hasNext()); + testNotifyManyEntityManyTimeMsgToEdgeServiceNever(new TbResource(), new TbResource(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ADDED, cntEntity); + Collections.sort(resources, idComparator); Collections.sort(loadedResources, idComparator); @@ -205,7 +258,7 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes PageData pageData; do { pageData = doGetTypedWithPageLink("/api/resource?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); loadedResources.addAll(pageData.getData()); if (pageData.hasNext()) { @@ -218,16 +271,23 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes Assert.assertEquals(resources, loadedResources); + Mockito.reset(tbClusterService, auditLogService); + + int cntEntity = resources.size(); for (TbResourceInfo resource : resources) { doDelete("/api/resource/" + resource.getId().getId().toString()) .andExpect(status().isOk()); } + testNotifyManyEntityManyTimeMsgToEdgeServiceNeverAdditionalInfoAny(new TbResource(), new TbResource(), + resources.get(0).getTenantId(), null, null, SYS_ADMIN_EMAIL, + ActionType.DELETED, cntEntity, 1); + pageLink = new PageLink(27); loadedResources.clear(); do { pageData = doGetTypedWithPageLink("/api/resource?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); loadedResources.addAll(pageData.getData()); if (pageData.hasNext()) { diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java index 2cc8bccc42..1840f1f8a0 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java @@ -26,6 +26,8 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentMatcher; +import org.mockito.Mockito; import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.servlet.ResultActions; import org.thingsboard.common.util.ThingsBoardExecutors; @@ -33,8 +35,10 @@ import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantInfo; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.queue.ProcessingStrategy; import org.thingsboard.server.common.data.queue.ProcessingStrategyType; import org.thingsboard.server.common.data.queue.Queue; @@ -56,6 +60,8 @@ import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.containsString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @TestPropertySource(properties = { @@ -86,17 +92,28 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { loginSysAdmin(); Tenant tenant = new Tenant(); tenant.setTitle("My tenant"); + + Mockito.reset(tbClusterService); + Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class); Assert.assertNotNull(savedTenant); Assert.assertNotNull(savedTenant.getId()); Assert.assertTrue(savedTenant.getCreatedTime() > 0); Assert.assertEquals(tenant.getTitle(), savedTenant.getTitle()); + + testBroadcastEntityStateChangeEventTimeManyTimeTenant(savedTenant, ComponentLifecycleEvent.CREATED, 1); + savedTenant.setTitle("My new tenant"); doPost("/api/tenant", savedTenant, Tenant.class); Tenant foundTenant = doGet("/api/tenant/" + savedTenant.getId().getId().toString(), Tenant.class); Assert.assertEquals(foundTenant.getTitle(), savedTenant.getTitle()); + + testBroadcastEntityStateChangeEventTimeManyTimeTenant(savedTenant, ComponentLifecycleEvent.UPDATED, 1); + doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) .andExpect(status().isOk()); + + testBroadcastEntityStateChangeEventTimeManyTimeTenant(savedTenant, ComponentLifecycleEvent.DELETED, 1); } @Test @@ -104,7 +121,14 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { loginSysAdmin(); Tenant tenant = new Tenant(); tenant.setTitle(RandomStringUtils.randomAlphanumeric(300)); - doPost("/api/tenant", tenant).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); + + Mockito.reset(tbClusterService); + + doPost("/api/tenant", tenant) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgErrorFieldLength("title")))); + + testBroadcastEntityStateChangeEventNeverTenant(); } @Test @@ -136,21 +160,31 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { @Test public void testSaveTenantWithEmptyTitle() throws Exception { loginSysAdmin(); + + Mockito.reset(tbClusterService); + Tenant tenant = new Tenant(); doPost("/api/tenant", tenant) .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Tenant title should be specified"))); + .andExpect(statusReason(containsString("Tenant title " + msgErrorShouldBeSpecified))); + + testBroadcastEntityStateChangeEventNeverTenant(); } @Test public void testSaveTenantWithInvalidEmail() throws Exception { loginSysAdmin(); + + Mockito.reset(tbClusterService); + Tenant tenant = new Tenant(); tenant.setTitle("My tenant"); tenant.setEmail("invalid@mail"); doPost("/api/tenant", tenant) .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString("Invalid email address format"))); + + testBroadcastEntityStateChangeEventNeverTenant(); } @Test @@ -159,10 +193,13 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { Tenant tenant = new Tenant(); tenant.setTitle("My tenant"); Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class); - doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) + + String tenantIdStr = savedTenant.getId().getId().toString(); + doDelete("/api/tenant/" + tenantIdStr) .andExpect(status().isOk()); - doGet("/api/tenant/" + savedTenant.getId().getId().toString()) - .andExpect(status().isNotFound()); + doGet("/api/tenant/" + tenantIdStr) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Tenant", tenantIdStr)))); } @Test @@ -175,8 +212,11 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { Assert.assertEquals(1, pageData.getData().size()); tenants.addAll(pageData.getData()); + Mockito.reset(tbClusterService); + + int cntEntity = 56; List> createFutures = new ArrayList<>(56); - for (int i = 0; i < 56; i++) { + for (int i = 0; i < cntEntity; i++) { Tenant tenant = new Tenant(); tenant.setTitle("Tenant" + i); createFutures.add(executor.submit(() -> @@ -184,6 +224,8 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { } tenants.addAll(Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS)); + testBroadcastEntityStateChangeEventTimeManyTimeTenant(new Tenant(), ComponentLifecycleEvent.CREATED, cntEntity); + List loadedTenants = new ArrayList<>(); pageLink = new PageLink(17); do { @@ -200,6 +242,8 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { .filter((t) -> !TEST_TENANT_NAME.equals(t.getTitle())) .collect(Collectors.toList()), executor).get(TIMEOUT, TimeUnit.SECONDS); + testBroadcastEntityStateChangeEventTimeManyTimeTenant(new Tenant(), ComponentLifecycleEvent.DELETED, cntEntity); + pageLink = new PageLink(17); pageData = doGetTypedWithPageLink("/api/tenants?", PAGE_DATA_TENANT_TYPE_REF, pageLink); Assert.assertFalse(pageData.hasNext()); @@ -464,7 +508,9 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { login(username, password); for (Queue queue : foundTenantQueues) { - doGet("/api/queues/" + queue.getId()).andExpect(status().isNotFound()); + doGet("/api/queues/" + queue.getId()) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNotFound))); } loginSysAdmin(); @@ -476,7 +522,7 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { queueConfiguration.setName(queueName); queueConfiguration.setTopic("tb_rule_engine." + queueName.toLowerCase()); queueConfiguration.setPollInterval(25); - queueConfiguration.setPartitions(new Random().nextInt(100)); + queueConfiguration.setPartitions(1 + new Random().nextInt(99)); queueConfiguration.setConsumerPerPartition(true); queueConfiguration.setPackProcessingTimeout(2000); SubmitStrategy submitStrategy = new SubmitStrategy(); @@ -519,4 +565,26 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { } return result; } + + private void testBroadcastEntityStateChangeEventTimeManyTimeTenant(Tenant tenant, ComponentLifecycleEvent event, int cntTime) { + ArgumentMatcher matcherTenant = cntTime == 1 ? argument -> argument.equals(tenant) : + argument -> argument.getClass().equals(Tenant.class); + if (ComponentLifecycleEvent.DELETED.equals(event)) { + Mockito.verify(tbClusterService, times( cntTime)).onTenantDelete(Mockito.argThat(matcherTenant), + Mockito.isNull()); + } else { + Mockito.verify(tbClusterService, times( cntTime)).onTenantChange(Mockito.argThat(matcherTenant), + Mockito.isNull()); + } + TenantId tenantId = cntTime == 1 ? tenant.getId() : (TenantId) createEntityId_NULL_UUID(tenant); + testBroadcastEntityStateChangeEventTime(tenantId, tenantId, cntTime); + Mockito.reset(tbClusterService); + } + + private void testBroadcastEntityStateChangeEventNeverTenant() { + Mockito.verify(tbClusterService, never()).onTenantChange(Mockito.any(Tenant.class), + Mockito.isNull()); + testBroadcastEntityStateChangeEventNever(createEntityId_NULL_UUID(new Tenant())); + Mockito.reset(tbClusterService); + } } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseTenantProfileControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseTenantProfileControllerTest.java index 1867577c23..11174618ed 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseTenantProfileControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseTenantProfileControllerTest.java @@ -17,24 +17,24 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.core.type.TypeReference; import org.apache.commons.lang3.RandomStringUtils; -import org.junit.After; import org.junit.Assert; import org.junit.Test; -import org.springframework.beans.factory.annotation.Autowired; +import org.mockito.ArgumentMatcher; +import org.mockito.Mockito; import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.id.TenantProfileId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.queue.ProcessingStrategy; import org.thingsboard.server.common.data.queue.ProcessingStrategyType; import org.thingsboard.server.common.data.queue.SubmitStrategy; import org.thingsboard.server.common.data.queue.SubmitStrategyType; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration; -import org.thingsboard.server.dao.tenant.TenantProfileService; import java.util.ArrayList; import java.util.Collections; @@ -42,6 +42,8 @@ import java.util.List; import java.util.stream.Collectors; import static org.hamcrest.Matchers.containsString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; public abstract class BaseTenantProfileControllerTest extends AbstractControllerTest { @@ -52,6 +54,9 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController @Test public void testSaveTenantProfile() throws Exception { loginSysAdmin(); + + Mockito.reset(tbClusterService); + TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); Assert.assertNotNull(savedTenantProfile); @@ -61,20 +66,30 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController Assert.assertEquals(tenantProfile.getDescription(), savedTenantProfile.getDescription()); Assert.assertEquals(tenantProfile.getProfileData(), savedTenantProfile.getProfileData()); Assert.assertEquals(tenantProfile.isDefault(), savedTenantProfile.isDefault()); - Assert.assertEquals(tenantProfile.isIsolatedTbCore(), savedTenantProfile.isIsolatedTbCore()); Assert.assertEquals(tenantProfile.isIsolatedTbRuleEngine(), savedTenantProfile.isIsolatedTbRuleEngine()); + testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(savedTenantProfile, ComponentLifecycleEvent.CREATED, 1); + savedTenantProfile.setName("New tenant profile"); doPost("/api/tenantProfile", savedTenantProfile, TenantProfile.class); TenantProfile foundTenantProfile = doGet("/api/tenantProfile/"+savedTenantProfile.getId().getId().toString(), TenantProfile.class); Assert.assertEquals(foundTenantProfile.getName(), savedTenantProfile.getName()); + + testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(savedTenantProfile, ComponentLifecycleEvent.UPDATED, 1); } @Test public void testSaveTenantProfileWithViolationOfLengthValidation() throws Exception { loginSysAdmin(); + + Mockito.reset(tbClusterService); + TenantProfile tenantProfile = this.createTenantProfile(RandomStringUtils.randomAlphabetic(300)); - doPost("/api/tenantProfile", tenantProfile).andExpect(statusReason(containsString("length of name must be equal or less than 255"))); + doPost("/api/tenantProfile", tenantProfile) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgErrorFieldLength("name")))); + + testBroadcastEntityStateChangeEventNeverTenantProfile(); } @Test @@ -122,9 +137,15 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController @Test public void testSaveTenantProfileWithEmptyName() throws Exception { loginSysAdmin(); + + Mockito.reset(tbClusterService); + TenantProfile tenantProfile = new TenantProfile(); doPost("/api/tenantProfile", tenantProfile).andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Tenant profile name should be specified"))); + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("Tenant profile name " + msgErrorShouldBeSpecified))); + + testBroadcastEntityStateChangeEventNeverTenantProfile(); } @Test @@ -132,9 +153,15 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController loginSysAdmin(); TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); doPost("/api/tenantProfile", tenantProfile).andExpect(status().isOk()); + + Mockito.reset(tbClusterService); + TenantProfile tenantProfile2 = this.createTenantProfile("Tenant Profile"); - doPost("/api/tenantProfile", tenantProfile2).andExpect(status().isBadRequest()) + doPost("/api/tenantProfile", tenantProfile2) + .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString("Tenant profile with such name already exists"))); + + testBroadcastEntityStateChangeEventNeverTenantProfile(); } @Test @@ -144,18 +171,14 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); savedTenantProfile.setIsolatedTbRuleEngine(true); addMainQueueConfig(savedTenantProfile); - doPost("/api/tenantProfile", savedTenantProfile).andExpect(status().isBadRequest()) + + Mockito.reset(tbClusterService); + + doPost("/api/tenantProfile", savedTenantProfile) + .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString("Can't update isolatedTbRuleEngine property"))); - } - @Test - public void testSaveSameTenantProfileWithDifferentIsolatedTbCore() throws Exception { - loginSysAdmin(); - TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); - TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); - savedTenantProfile.setIsolatedTbCore(true); - doPost("/api/tenantProfile", savedTenantProfile).andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Can't update isolatedTbCore property"))); + testBroadcastEntityStateChangeEventNeverTenantProfile(); } @Test @@ -169,10 +192,14 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController tenant.setTenantProfileId(savedTenantProfile.getId()); Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class); + Mockito.reset(tbClusterService); + doDelete("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString()) .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString("The tenant profile referenced by the tenants cannot be deleted"))); + testBroadcastEntityStateChangeEventNeverTenantProfile(); + doDelete("/api/tenant/"+savedTenant.getId().getId().toString()) .andExpect(status().isOk()); } @@ -183,11 +210,16 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); + Mockito.reset(tbClusterService); + doDelete("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString()) .andExpect(status().isOk()); + testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(savedTenantProfile, ComponentLifecycleEvent.DELETED, 1); + doGet("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString()) - .andExpect(status().isNotFound()); + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Tenant profile", savedTenantProfile.getId().getId().toString())))); } @Test @@ -196,21 +228,26 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController List tenantProfiles = new ArrayList<>(); PageLink pageLink = new PageLink(17); PageData pageData = doGetTypedWithPageLink("/api/tenantProfiles?", - new TypeReference>(){}, pageLink); + new TypeReference<>(){}, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(1, pageData.getTotalElements()); tenantProfiles.addAll(pageData.getData()); + Mockito.reset(tbClusterService); + + int cntEntity = 28; for (int i=0;i<28;i++) { TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"+i); tenantProfiles.add(doPost("/api/tenantProfile", tenantProfile, TenantProfile.class)); } + testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(new TenantProfile(), ComponentLifecycleEvent.CREATED, cntEntity); + List loadedTenantProfiles = new ArrayList<>(); pageLink = new PageLink(17); do { pageData = doGetTypedWithPageLink("/api/tenantProfiles?", - new TypeReference>(){}, pageLink); + new TypeReference<>(){}, pageLink); loadedTenantProfiles.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -222,6 +259,8 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController Assert.assertEquals(tenantProfiles, loadedTenantProfiles); + Mockito.reset(tbClusterService); + for (TenantProfile tenantProfile : loadedTenantProfiles) { if (!tenantProfile.isDefault()) { doDelete("/api/tenantProfile/" + tenantProfile.getId().getId().toString()) @@ -234,6 +273,8 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController new TypeReference>(){}, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(1, pageData.getTotalElements()); + + testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(new TenantProfile(), ComponentLifecycleEvent.DELETED, cntEntity); } @Test @@ -242,7 +283,7 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController List tenantProfiles = new ArrayList<>(); PageLink pageLink = new PageLink(17); PageData tenantProfilePageData = doGetTypedWithPageLink("/api/tenantProfiles?", - new TypeReference>(){}, pageLink); + new TypeReference<>(){}, pageLink); Assert.assertFalse(tenantProfilePageData.hasNext()); Assert.assertEquals(1, tenantProfilePageData.getTotalElements()); tenantProfiles.addAll(tenantProfilePageData.getData()); @@ -294,7 +335,6 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController tenantProfileData.setConfiguration(new DefaultTenantProfileConfiguration()); tenantProfile.setProfileData(tenantProfileData); tenantProfile.setDefault(false); - tenantProfile.setIsolatedTbCore(false); tenantProfile.setIsolatedTbRuleEngine(false); return tenantProfile; } @@ -322,4 +362,28 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController profileData.setQueueConfiguration(Collections.singletonList(mainQueueConfiguration)); tenantProfile.setProfileData(profileData); } + + + private void testBroadcastEntityStateChangeEventTimeManyTimeTenantProfile(TenantProfile tenantProfile, ComponentLifecycleEvent event, int cntTime) { + ArgumentMatcher matcherTenantProfile = cntTime == 1 ? argument -> argument.equals(tenantProfile) : + argument -> argument.getClass().equals(TenantProfile.class); + if (ComponentLifecycleEvent.DELETED.equals(event)) { + Mockito.verify(tbClusterService, times( cntTime)).onTenantProfileDelete(Mockito.argThat( matcherTenantProfile), + Mockito.isNull()); + testBroadcastEntityStateChangeEventNever(createEntityId_NULL_UUID(new Tenant())); + } else { + Mockito.verify(tbClusterService, times( cntTime)).onTenantProfileChange(Mockito.argThat(matcherTenantProfile), + Mockito.isNull()); + TenantProfileId tenantProfileIdId = cntTime == 1 ? tenantProfile.getId() : (TenantProfileId) createEntityId_NULL_UUID(tenantProfile); + testBroadcastEntityStateChangeEventTime(tenantProfileIdId, null, cntTime); + } + Mockito.reset(tbClusterService); + } + + private void testBroadcastEntityStateChangeEventNeverTenantProfile() { + Mockito.verify(tbClusterService, never()).onTenantProfileChange(Mockito.any(TenantProfile.class), + Mockito.isNull()); + testBroadcastEntityStateChangeEventNever(createEntityId_NULL_UUID(new Tenant())); + Mockito.reset(tbClusterService, auditLogService); + } } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java index 5b917ea8fa..6e8c15cc0e 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java @@ -21,15 +21,18 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang3.RandomStringUtils; import org.junit.Assert; import org.junit.Test; +import org.mockito.Mockito; import org.springframework.http.HttpHeaders; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.service.mail.TestMailService; import java.util.ArrayList; @@ -42,11 +45,14 @@ import static org.hamcrest.Matchers.is; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.dao.model.ModelConstants.SYSTEM_TENANT; public abstract class BaseUserControllerTest extends AbstractControllerTest { private IdComparator idComparator = new IdComparator<>(); + private CustomerId customerNUULId = (CustomerId) createEntityId_NULL_UUID(new Customer()); + @Test public void testSaveUser() throws Exception { loginSysAdmin(); @@ -58,6 +64,9 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { user.setEmail(email); user.setFirstName("Joe"); user.setLastName("Downs"); + + Mockito.reset(tbClusterService, auditLogService); + User savedUser = doPost("/api/user", user, User.class); Assert.assertNotNull(savedUser); Assert.assertNotNull(savedUser.getId()); @@ -67,6 +76,11 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { User foundUser = doGet("/api/user/" + savedUser.getId().getId().toString(), User.class); Assert.assertEquals(foundUser, savedUser); + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(foundUser, foundUser, + SYSTEM_TENANT, customerNUULId, null, SYS_ADMIN_EMAIL, + ActionType.ADDED, ActionType.ADDED, 1, 1, 1); + Mockito.reset(tbClusterService, auditLogService); + logout(); doGet("/api/noauth/activate?activateToken={activateToken}", TestMailService.currentActivateToken) .andExpect(status().isSeeOther()) @@ -94,14 +108,24 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { .andExpect(jsonPath("$.email", is(email))); loginSysAdmin(); + foundUser = doGet("/api/user/" + savedUser.getId().getId().toString(), User.class); + + Mockito.reset(tbClusterService, auditLogService); + doDelete("/api/user/" + savedUser.getId().getId().toString()) .andExpect(status().isOk()); + + testNotifyEntityOneTimeMsgToEdgeServiceNever(foundUser, foundUser.getId(), foundUser.getId(), + SYSTEM_TENANT, customerNUULId, null, SYS_ADMIN_EMAIL, + ActionType.DELETED, foundUser.getId().getId().toString()); } @Test public void testSaveUserWithViolationOfFiledValidation() throws Exception { loginSysAdmin(); + Mockito.reset(tbClusterService, auditLogService); + String email = "tenant2@thingsboard.org"; User user = new User(); user.setAuthority(Authority.TENANT_ADMIN); @@ -109,10 +133,26 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { user.setEmail(email); user.setFirstName(RandomStringUtils.randomAlphabetic(300)); user.setLastName("Downs"); - doPost("/api/user", user).andExpect(statusReason(containsString("Validation error: length of first name must be equal or less than 255"))); + String msgError = msgErrorFieldLength("first name"); + doPost("/api/user", user) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); + + testNotifyEntityEqualsOneTimeServiceNeverError(user, + SYSTEM_TENANT, null, SYS_ADMIN_EMAIL, + ActionType.ADDED, new DataValidationException(msgError)); + Mockito.reset(tbClusterService, auditLogService); + user.setFirstName("Normal name"); + msgError = msgErrorFieldLength("last name"); user.setLastName(RandomStringUtils.randomAlphabetic(300)); - doPost("/api/user", user).andExpect(statusReason(containsString("length of last name must be equal or less than 255"))); + doPost("/api/user", user) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); + + testNotifyEntityEqualsOneTimeServiceNeverError(user, + SYSTEM_TENANT, null, SYS_ADMIN_EMAIL, + ActionType.ADDED, new DataValidationException(msgError)); } @Test @@ -128,9 +168,16 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); loginDifferentTenant(); - doPost("/api/user", tenantAdmin, User.class, status().isForbidden()); - deleteDifferentTenant(); + Mockito.reset(tbClusterService, auditLogService); + + doPost("/api/user", tenantAdmin) + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); + + testNotifyEntityNever(tenantAdmin.getId(), tenantAdmin); + + deleteDifferentTenant(); } @Test @@ -162,7 +209,9 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { .put("resetToken", TestMailService.currentResetPasswordToken) .put("password", "testPassword2"); - JsonNode tokenInfo = readResponse(doPost("/api/noauth/resetPassword", resetPasswordRequest).andExpect(status().isOk()), JsonNode.class); + JsonNode tokenInfo = readResponse( + doPost("/api/noauth/resetPassword", resetPasswordRequest) + .andExpect(status().isOk()), JsonNode.class); validateAndSetJwtToken(tokenInfo, email); doGet("/api/auth/user") @@ -205,6 +254,8 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { public void testSaveUserWithSameEmail() throws Exception { loginSysAdmin(); + Mockito.reset(tbClusterService, auditLogService); + String email = TENANT_ADMIN_EMAIL; User user = new User(); user.setAuthority(Authority.TENANT_ADMIN); @@ -213,15 +264,22 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { user.setFirstName("Joe"); user.setLastName("Downs"); + String msgError = "User with email '" + email + "' already present in database"; doPost("/api/user", user) .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("User with email '" + email + "' already present in database"))); + .andExpect(statusReason(containsString(msgError))); + + testNotifyEntityEqualsOneTimeServiceNeverError(user, + SYSTEM_TENANT, null, SYS_ADMIN_EMAIL, + ActionType.ADDED, new DataValidationException(msgError)); } @Test public void testSaveUserWithInvalidEmail() throws Exception { loginSysAdmin(); + Mockito.reset(tbClusterService, auditLogService); + String email = "tenant_thingsboard.org"; User user = new User(); user.setAuthority(Authority.TENANT_ADMIN); @@ -230,39 +288,59 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { user.setFirstName("Joe"); user.setLastName("Downs"); + String msgError = "Invalid email address format '" + email + "'"; doPost("/api/user", user) .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Invalid email address format '" + email + "'"))); + .andExpect(statusReason(containsString(msgError))); + + testNotifyEntityEqualsOneTimeServiceNeverError(user, + SYSTEM_TENANT, null, SYS_ADMIN_EMAIL, + ActionType.ADDED, new DataValidationException(msgError)); } @Test public void testSaveUserWithEmptyEmail() throws Exception { loginSysAdmin(); + Mockito.reset(tbClusterService, auditLogService); + User user = new User(); user.setAuthority(Authority.TENANT_ADMIN); user.setTenantId(tenantId); user.setFirstName("Joe"); user.setLastName("Downs"); + String msgError = "User email " + msgErrorShouldBeSpecified; doPost("/api/user", user) .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("User email should be specified"))); + .andExpect(statusReason(containsString("User email " + msgErrorShouldBeSpecified))); + + testNotifyEntityEqualsOneTimeServiceNeverError(user, + SYSTEM_TENANT, null, SYS_ADMIN_EMAIL, + ActionType.ADDED, new DataValidationException(msgError)); } @Test public void testSaveUserWithoutTenant() throws Exception { loginSysAdmin(); + Mockito.reset(tbClusterService, auditLogService); + User user = new User(); user.setAuthority(Authority.TENANT_ADMIN); user.setEmail("tenant2@thingsboard.org"); user.setFirstName("Joe"); user.setLastName("Downs"); + String msgError = "Tenant administrator should be assigned to tenant"; doPost("/api/user", user) .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Tenant administrator should be assigned to tenant"))); + .andExpect(statusReason(containsString(msgError))); + + testNotifyEntityEqualsOneTimeServiceNeverError(user, + SYSTEM_TENANT, null, SYS_ADMIN_EMAIL, + ActionType.ADDED, new DataValidationException(msgError)); + } @Test @@ -284,8 +362,10 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { doDelete("/api/user/" + savedUser.getId().getId().toString()) .andExpect(status().isOk()); - doGet("/api/user/" + savedUser.getId().getId().toString()) - .andExpect(status().isNotFound()); + String userIdStr = savedUser.getId().getId().toString(); + doGet("/api/user/" + userIdStr) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString( msgErrorNoFound("User",userIdStr)))); } @Test @@ -300,8 +380,11 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { TenantId tenantId = savedTenant.getId(); + Mockito.reset(tbClusterService, auditLogService); + + int cntEntity = 64; List tenantAdmins = new ArrayList<>(); - for (int i = 0; i < 64; i++) { + for (int i = 0; i < cntEntity; i++) { User user = new User(); user.setAuthority(Authority.TENANT_ADMIN); user.setTenantId(tenantId); @@ -309,12 +392,18 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { tenantAdmins.add(doPost("/api/user", user, User.class)); } + User testManyUser = new User(); + testManyUser.setTenantId(tenantId); + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(testManyUser, testManyUser, + SYSTEM_TENANT, customerNUULId, null, SYS_ADMIN_EMAIL, + ActionType.ADDED, ActionType.ADDED, cntEntity, cntEntity, cntEntity); + List loadedTenantAdmins = new ArrayList<>(); PageLink pageLink = new PageLink(33); PageData pageData = null; do { pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); loadedTenantAdmins.addAll(pageData.getData()); if (pageData.hasNext()) { @@ -333,7 +422,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { pageLink = new PageLink(33); pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertTrue(pageData.getData().isEmpty()); @@ -377,7 +466,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { PageData pageData = null; do { pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); loadedTenantAdminsEmail1.addAll(pageData.getData()); if (pageData.hasNext()) { @@ -394,7 +483,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { pageLink = new PageLink(16, 0, email2); do { pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); loadedTenantAdminsEmail2.addAll(pageData.getData()); if (pageData.hasNext()) { @@ -407,14 +496,22 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { Assert.assertEquals(tenantAdminsEmail2, loadedTenantAdminsEmail2); + Mockito.reset(tbClusterService, auditLogService); + + int cntEntity = loadedTenantAdminsEmail1.size(); for (User user : loadedTenantAdminsEmail1) { doDelete("/api/user/" + user.getId().getId().toString()) .andExpect(status().isOk()); } + User testManyUser = new User(); + testManyUser.setTenantId(tenantId); + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(testManyUser, testManyUser, + SYSTEM_TENANT, customerNUULId, null, SYS_ADMIN_EMAIL, + ActionType.DELETED, ActionType.DELETED, cntEntity, 0, cntEntity, new String()); pageLink = new PageLink(4, 0, email1); pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -426,7 +523,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, email2); pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -443,7 +540,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { tenantAdmin.setFirstName("Joe"); tenantAdmin.setLastName("Downs"); - tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); + createUserAndLogin(tenantAdmin, "testPassword1"); Customer customer = new Customer(); customer.setTitle("My customer"); @@ -465,7 +562,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { PageData pageData = null; do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); loadedCustomerUsers.addAll(pageData.getData()); if (pageData.hasNext()) { @@ -493,7 +590,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { tenantAdmin.setFirstName("Joe"); tenantAdmin.setLastName("Downs"); - tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); + createUserAndLogin(tenantAdmin, "testPassword1"); Customer customer = new Customer(); customer.setTitle("My customer"); @@ -531,10 +628,10 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { List loadedCustomerUsersEmail1 = new ArrayList<>(); PageLink pageLink = new PageLink(33, 0, email1); - PageData pageData = null; + PageData pageData; do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); loadedCustomerUsersEmail1.addAll(pageData.getData()); if (pageData.hasNext()) { @@ -551,7 +648,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { pageLink = new PageLink(16, 0, email2); do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); loadedCustomerUsersEmail2.addAll(pageData.getData()); if (pageData.hasNext()) { @@ -571,7 +668,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, email1); pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -583,7 +680,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, email2); pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -591,5 +688,4 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { doDelete("/api/customer/" + customerId.getId().toString()) .andExpect(status().isOk()); } - } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseWidgetsBundleControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseWidgetsBundleControllerTest.java index 21acf5ee81..68ed3636e9 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseWidgetsBundleControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseWidgetsBundleControllerTest.java @@ -21,8 +21,12 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; +import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; @@ -34,6 +38,7 @@ import java.util.List; import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.dao.model.ModelConstants.SYSTEM_TENANT; public abstract class BaseWidgetsBundleControllerTest extends AbstractControllerTest { @@ -73,8 +78,16 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController public void testSaveWidgetsBundle() throws Exception { WidgetsBundle widgetsBundle = new WidgetsBundle(); widgetsBundle.setTitle("My widgets bundle"); + + Mockito.reset(tbClusterService); + WidgetsBundle savedWidgetsBundle = doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class); + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(savedWidgetsBundle, savedWidgetsBundle, + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ADDED, ActionType.ADDED, 0, 1, 0); + Mockito.reset(tbClusterService); + Assert.assertNotNull(savedWidgetsBundle); Assert.assertNotNull(savedWidgetsBundle.getId()); Assert.assertNotNull(savedWidgetsBundle.getAlias()); @@ -87,13 +100,25 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController WidgetsBundle foundWidgetsBundle = doGet("/api/widgetsBundle/" + savedWidgetsBundle.getId().getId().toString(), WidgetsBundle.class); Assert.assertEquals(foundWidgetsBundle.getTitle(), savedWidgetsBundle.getTitle()); + + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(savedWidgetsBundle, savedWidgetsBundle, + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.UPDATED, ActionType.UPDATED, 0, 1, 0); } @Test public void testSaveWidgetBundleWithViolationOfLengthValidation() throws Exception { WidgetsBundle widgetsBundle = new WidgetsBundle(); widgetsBundle.setTitle(RandomStringUtils.randomAlphabetic(300)); - doPost("/api/widgetsBundle", widgetsBundle).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); + + Mockito.reset(tbClusterService); + + String msgError = msgErrorFieldLength("title"); + doPost("/api/widgetsBundle", widgetsBundle) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); + + testNotifyEntityNever(widgetsBundle.getId(), widgetsBundle); } @Test @@ -103,7 +128,15 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController WidgetsBundle savedWidgetsBundle = doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class); loginDifferentTenant(); - doPost("/api/widgetsBundle", savedWidgetsBundle, WidgetsBundle.class, status().isForbidden()); + + Mockito.reset(tbClusterService); + + doPost("/api/widgetsBundle", savedWidgetsBundle) + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); + + testNotifyEntityNever(savedWidgetsBundle.getId(), savedWidgetsBundle); + deleteDifferentTenant(); } @@ -121,21 +154,35 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController public void testDeleteWidgetsBundle() throws Exception { WidgetsBundle widgetsBundle = new WidgetsBundle(); widgetsBundle.setTitle("My widgets bundle"); + + Mockito.reset(tbClusterService, auditLogService); + WidgetsBundle savedWidgetsBundle = doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class); doDelete("/api/widgetsBundle/"+savedWidgetsBundle.getId().getId().toString()) .andExpect(status().isOk()); - doGet("/api/widgetsBundle/"+savedWidgetsBundle.getId().getId().toString()) - .andExpect(status().isNotFound()); + String savedWidgetsBundleIdStr = savedWidgetsBundle.getId().getId().toString(); + doGet("/api/widgetsBundle/" + savedWidgetsBundleIdStr) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Widgets bundle", savedWidgetsBundleIdStr)))); + + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(savedWidgetsBundle, savedWidgetsBundle, + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.DELETED, ActionType.DELETED, 0, 1, 0); } @Test public void testSaveWidgetsBundleWithEmptyTitle() throws Exception { + + Mockito.reset(tbClusterService, auditLogService); + WidgetsBundle widgetsBundle = new WidgetsBundle(); doPost("/api/widgetsBundle", widgetsBundle) .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Widgets bundle title should be specified"))); + .andExpect(statusReason(containsString("Widgets bundle title " + msgErrorShouldBeSpecified))); + + testNotifyEntityNever(widgetsBundle.getId(), widgetsBundle); } @Test @@ -144,10 +191,14 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController widgetsBundle.setTitle("My widgets bundle"); WidgetsBundle savedWidgetsBundle = doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class); savedWidgetsBundle.setAlias("new_alias"); + + Mockito.reset(tbClusterService); + doPost("/api/widgetsBundle", savedWidgetsBundle) .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString("Update of widgets bundle alias is prohibited"))); + testNotifyEntityNever(savedWidgetsBundle.getId(), savedWidgetsBundle); } @Test @@ -156,16 +207,22 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController login(tenantAdmin.getEmail(), "testPassword1"); List sysWidgetsBundles = doGetTyped("/api/widgetsBundles?", - new TypeReference>(){}); + new TypeReference<>(){}); + Mockito.reset(tbClusterService); + int cntEntity = 73; List widgetsBundles = new ArrayList<>(); - for (int i=0;i<73;i++) { + for (int i=0;i loadedWidgetsBundles = new ArrayList<>(); @@ -173,7 +230,7 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController PageData pageData; do { pageData = doGetTypedWithPageLink("/api/widgetsBundles?", - new TypeReference>(){}, pageLink); + new TypeReference<>(){}, pageLink); loadedWidgetsBundles.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -192,10 +249,11 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController loginSysAdmin(); List sysWidgetsBundles = doGetTyped("/api/widgetsBundles?", - new TypeReference>(){}); + new TypeReference<>(){}); + int cntEntity = 120; List createdWidgetsBundles = new ArrayList<>(); - for (int i=0;i<120;i++) { + for (int i=0;i pageData; do { pageData = doGetTypedWithPageLink("/api/widgetsBundles?", - new TypeReference>(){}, pageLink); + new TypeReference<>(){}, pageLink); loadedWidgetsBundles.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -221,11 +279,17 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController Assert.assertEquals(widgetsBundles, loadedWidgetsBundles); + Mockito.reset(tbClusterService); + for (WidgetsBundle widgetsBundle : createdWidgetsBundles) { doDelete("/api/widgetsBundle/"+widgetsBundle.getId().getId().toString()) .andExpect(status().isOk()); } + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new WidgetsBundle(), new WidgetsBundle(), + SYSTEM_TENANT, (CustomerId) createEntityId_NULL_UUID(new Customer()), null, SYS_ADMIN_EMAIL, + ActionType.DELETED, ActionType.DELETED, 0, cntEntity, 0); + pageLink = new PageLink(17); loadedWidgetsBundles.clear(); do { @@ -262,7 +326,7 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController widgetsBundles.addAll(sysWidgetsBundles); List loadedWidgetsBundles = doGetTyped("/api/widgetsBundles?", - new TypeReference>(){}); + new TypeReference<>(){}); Collections.sort(widgetsBundles, idComparator); Collections.sort(loadedWidgetsBundles, idComparator); @@ -277,7 +341,7 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController List sysWidgetsBundles = doGetTyped("/api/widgetsBundles?", - new TypeReference>(){}); + new TypeReference<>(){}); List createdSystemWidgetsBundles = new ArrayList<>(); for (int i=0;i<82;i++) { @@ -324,7 +388,7 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController } loadedWidgetsBundles = doGetTyped("/api/widgetsBundles?", - new TypeReference>(){}); + new TypeReference<>(){}); Collections.sort(sysWidgetsBundles, idComparator); Collections.sort(loadedWidgetsBundles, idComparator); diff --git a/application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java index c0276df82f..4bcb9c6d59 100644 --- a/application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java @@ -163,7 +163,6 @@ abstract public class BaseEdgeTest extends AbstractControllerTest { private Tenant savedTenant; private TenantId tenantId; private User tenantAdmin; - private QueueId defaultQueueId; private DeviceProfile thermostatDeviceProfile; @@ -186,8 +185,6 @@ abstract public class BaseEdgeTest extends AbstractControllerTest { tenantId = savedTenant.getId(); Assert.assertNotNull(savedTenant); - defaultQueueId = getRandomQueueId(); - tenantAdmin = new User(); tenantAdmin.setAuthority(Authority.TENANT_ADMIN); tenantAdmin.setTenantId(savedTenant.getId()); @@ -378,7 +375,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest { @Test public void testDeviceProfiles() throws Exception { // 1 - DeviceProfile deviceProfile = this.createDeviceProfile("ONE_MORE_DEVICE_PROFILE", null, defaultQueueId); + DeviceProfile deviceProfile = this.createDeviceProfile("ONE_MORE_DEVICE_PROFILE", null); extendDeviceProfileData(deviceProfile); edgeImitator.expectMessageAmount(1); deviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); @@ -389,8 +386,6 @@ abstract public class BaseEdgeTest extends AbstractControllerTest { Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType()); Assert.assertEquals(deviceProfileUpdateMsg.getIdMSB(), deviceProfile.getUuidId().getMostSignificantBits()); Assert.assertEquals(deviceProfileUpdateMsg.getIdLSB(), deviceProfile.getUuidId().getLeastSignificantBits()); - Assert.assertEquals(defaultQueueId.getId().getMostSignificantBits(), deviceProfileUpdateMsg.getDefaultQueueIdMSB()); - Assert.assertEquals(defaultQueueId.getId().getLeastSignificantBits(), deviceProfileUpdateMsg.getDefaultQueueIdLSB()); // 2 edgeImitator.expectMessageAmount(1); diff --git a/application/src/test/java/org/thingsboard/server/service/cluster/routing/HashPartitionServiceTest.java b/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java similarity index 66% rename from application/src/test/java/org/thingsboard/server/service/cluster/routing/HashPartitionServiceTest.java rename to application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java index 9df6c16111..88aa45c74c 100644 --- a/application/src/test/java/org/thingsboard/server/service/cluster/routing/HashPartitionServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java @@ -13,9 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.cluster.routing; +package org.thingsboard.server.queue.discovery; +import com.datastax.driver.core.utils.UUIDs; import com.datastax.oss.driver.api.core.uuid.Uuids; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.Before; @@ -29,17 +31,16 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.queue.discovery.HashPartitionService; -import org.thingsboard.server.queue.discovery.QueueRoutingInfoService; -import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; -import org.thingsboard.server.queue.discovery.TenantRoutingInfoService; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Random; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static org.mockito.Mockito.mock; @@ -111,15 +112,56 @@ public class HashPartitionServiceTest { map.put(partition, map.getOrDefault(partition, 0) + 1); } - List> data = map.entrySet().stream().sorted(Comparator.comparingInt(Map.Entry::getValue)).collect(Collectors.toList()); + checkDispersion(start, map, ITERATIONS, 5.0); + } + + @SneakyThrows + @Test + public void testDispersionOnResolveByPartitionIdx() { + int serverCount = 5; + int tenantCount = 1000; + int queueCount = 3; + int partitionCount = 3; + + List services = new ArrayList<>(); + + for (int i = 0; i < serverCount; i++) { + services.add(TransportProtos.ServiceInfo.newBuilder().setServiceId("RE-" + i).build()); + } + + long start = System.currentTimeMillis(); + Map map = new HashMap<>(); + services.forEach(s -> map.put(s.getServiceId(), 0)); + + Random random = new Random(); + long ts = new SimpleDateFormat("dd-MM-yyyy").parse("06-12-2016").getTime() - TimeUnit.DAYS.toMillis(tenantCount); + for (int tenantIndex = 0; tenantIndex < tenantCount; tenantIndex++) { + TenantId tenantId = new TenantId(UUIDs.startOf(ts)); + ts += TimeUnit.DAYS.toMillis(1) + random.nextInt(1000); + for (int queueIndex = 0; queueIndex < queueCount; queueIndex++) { + QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, "queue" + queueIndex, tenantId); + for (int partition = 0; partition < partitionCount; partition++) { + TransportProtos.ServiceInfo serviceInfo = clusterRoutingService.resolveByPartitionIdx(services, queueKey, partition); + String serviceId = serviceInfo.getServiceId(); + map.put(serviceId, map.get(serviceId) + 1); + } + } + } + + checkDispersion(start, map, tenantCount * queueCount * partitionCount, 10.0); + } + + private void checkDispersion(long start, Map map, int iterations, double maxDiffPercent) { + List> data = map.entrySet().stream().sorted(Comparator.comparingInt(Map.Entry::getValue)).collect(Collectors.toList()); long end = System.currentTimeMillis(); - double diff = (data.get(data.size() - 1).getValue() - data.get(0).getValue()); - double diffPercent = (diff / ITERATIONS) * 100.0; + double ideal = ((double) iterations) / map.size(); + double diff = Math.max(data.get(data.size() - 1).getValue() - ideal, ideal - data.get(0).getValue()); + double diffPercent = (diff / ideal) * 100.0; System.out.println("Time: " + (end - start) + " Diff: " + diff + "(" + String.format("%f", diffPercent) + "%)"); - Assert.assertTrue(diffPercent < 0.5); - for (Map.Entry entry : data) { + for (Map.Entry entry : data) { System.out.println(entry.getKey() + ": " + entry.getValue()); } + Assert.assertTrue(diffPercent < maxDiffPercent); } } diff --git a/application/src/test/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructorTest.java b/application/src/test/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructorTest.java index 42b8c628cc..60ebd78b8c 100644 --- a/application/src/test/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructorTest.java +++ b/application/src/test/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructorTest.java @@ -23,53 +23,37 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.id.QueueId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.data.rule.NodeConnectionInfo; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleNode; -import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.RuleChainConnectionInfoProto; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleNodeProto; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.service.edge.rpc.constructor.rule.RuleChainMetadataConstructorV333; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.UUID; -import static org.mockito.Mockito.mock; - @Slf4j @RunWith(MockitoJUnitRunner.class) public class RuleChainMsgConstructorTest { private RuleChainMsgConstructor constructor; - private QueueService queueService; - private TenantId tenantId; - private String queueId = "af588000-6c7c-11ec-bafd-c9a47a5c8d99"; - @Before public void setup() { - queueService = mock(QueueService.class); - constructor = new RuleChainMsgConstructor(queueService); + constructor = new RuleChainMsgConstructor(); tenantId = new TenantId(UUID.randomUUID()); - - Queue queue = new Queue(); - queue.setName("HighPriority"); - Mockito.when(queueService.findQueueById(tenantId, new QueueId(UUID.fromString(queueId)))).thenReturn(queue); } @Test @@ -88,7 +72,7 @@ public class RuleChainMsgConstructorTest { assertCheckpointRuleNodeConfiguration( ruleChainMetadataUpdateMsg.getNodesList(), - "{\"queueId\":\"" + queueId + "\"}"); + "{\"queueName\":\"HighPriority\"}"); } @Test @@ -345,7 +329,7 @@ public class RuleChainMsgConstructorTest { return createRuleNode(ruleChainId, "org.thingsboard.rule.engine.flow.TbCheckpointNode", "Checkpoint node", - JacksonUtil.OBJECT_MAPPER.readTree("{\"queueId\":\"" + queueId + "\"}"), + JacksonUtil.OBJECT_MAPPER.readTree("{\"queueName\":\"HighPriority\"}"), JacksonUtil.OBJECT_MAPPER.readTree("{\"description\":\"\",\"layoutX\":178,\"layoutY\":647}")); } diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/attributes/AbstractCoapAttributesIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/AbstractCoapAttributesIntegrationTest.java index 9710c82866..cd9ed239a5 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/attributes/AbstractCoapAttributesIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/AbstractCoapAttributesIntegrationTest.java @@ -46,11 +46,11 @@ import org.thingsboard.server.transport.coap.CoapTestClient; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -235,25 +235,37 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap CoapTestCallback callbackCoap = new CoapTestCallback(1); CoapObserveRelation observeRelation = client.getObserveRelation(callbackCoap); - callbackCoap.getLatch().await(3, TimeUnit.SECONDS); - + String awaitAlias = "await Json Test Subscribe To AttributesUpdates (client.getObserveRelation)"; + await(awaitAlias) + .atMost(10, TimeUnit.SECONDS) + .until(() -> CoAP.ResponseCode.CONTENT.equals(callbackCoap.getResponseCode()) && + callbackCoap.getObserve() != null && + 0 == callbackCoap.getObserve().intValue()); if (emptyCurrentStateNotification) { - validateUpdateAttributesJsonResponse(callbackCoap, "{}", 0); + validateUpdateAttributesJsonResponse(callbackCoap, "{}"); } else { - validateUpdateAttributesJsonResponse(callbackCoap, SHARED_ATTRIBUTES_PAYLOAD_ON_CURRENT_STATE_NOTIFICATION, 0); + validateUpdateAttributesJsonResponse(callbackCoap, SHARED_ATTRIBUTES_PAYLOAD_ON_CURRENT_STATE_NOTIFICATION); } - CountDownLatch latch = new CountDownLatch(1); - int expectedObserveCnt = callbackCoap.getObserve().intValue() + 1; + int expectedObserveForAttributesUpdate = callbackCoap.getObserve().intValue() + 1; doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", SHARED_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); - latch.await(3, TimeUnit.SECONDS); - validateUpdateAttributesJsonResponse(callbackCoap, SHARED_ATTRIBUTES_PAYLOAD, expectedObserveCnt); - - latch = new CountDownLatch(1); - int expectedObserveBeforeDeleteCnt = callbackCoap.getObserve().intValue() + 1; + awaitAlias = "await Json Test Subscribe To AttributesUpdates (add attributes)"; + await(awaitAlias) + .atMost(10, TimeUnit.SECONDS) + .until(() -> CoAP.ResponseCode.CONTENT.equals(callbackCoap.getResponseCode()) && + callbackCoap.getObserve() != null && + expectedObserveForAttributesUpdate == callbackCoap.getObserve().intValue()); + validateUpdateAttributesJsonResponse(callbackCoap, SHARED_ATTRIBUTES_PAYLOAD); + + int expectedObserveForAttributesDelete = callbackCoap.getObserve().intValue() + 1; doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=sharedJson", String.class); - latch.await(3, TimeUnit.SECONDS); - validateUpdateAttributesJsonResponse(callbackCoap, SHARED_ATTRIBUTES_DELETED_RESPONSE, expectedObserveBeforeDeleteCnt); + awaitAlias = "await Json Test Subscribe To AttributesUpdates (deleted attributes)"; + await(awaitAlias) + .atMost(10, TimeUnit.SECONDS) + .until(() -> CoAP.ResponseCode.CONTENT.equals(callbackCoap.getResponseCode()) && + callbackCoap.getObserve() != null && + expectedObserveForAttributesDelete == callbackCoap.getObserve().intValue()); + validateUpdateAttributesJsonResponse(callbackCoap, SHARED_ATTRIBUTES_DELETED_RESPONSE); observeRelation.proactiveCancel(); assertTrue(observeRelation.isCanceled()); @@ -269,8 +281,13 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap client = new CoapTestClient(accessToken, FeatureType.ATTRIBUTES); CoapTestCallback callbackCoap = new CoapTestCallback(1); + String awaitAlias = "await Proto Test Subscribe To Attributes Updates (add attributes)"; CoapObserveRelation observeRelation = client.getObserveRelation(callbackCoap); - callbackCoap.getLatch().await(3, TimeUnit.SECONDS); + await(awaitAlias) + .atMost(10, TimeUnit.SECONDS) + .until(() -> CoAP.ResponseCode.CONTENT.equals(callbackCoap.getResponseCode()) && + callbackCoap.getObserve() != null && + 0 == callbackCoap.getObserve().intValue()); if (emptyCurrentStateNotification) { validateEmptyCurrentStateAttributesProtoResponse(callbackCoap); @@ -278,17 +295,25 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap validateCurrentStateAttributesProtoResponse(callbackCoap); } - CountDownLatch latch = new CountDownLatch(1); - int expectedObserveCnt = callbackCoap.getObserve().intValue() + 1; + int expectedObserveForAttributesUpdate = callbackCoap.getObserve().intValue() + 1; doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", SHARED_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); - latch.await(3, TimeUnit.SECONDS); - validateUpdateProtoAttributesResponse(callbackCoap, expectedObserveCnt); - - latch = new CountDownLatch(1); - int expectedObserveBeforeDeleteCnt = callbackCoap.getObserve().intValue() + 1; + awaitAlias = "await Proto Test Subscribe To Attributes Updates (add attributes)"; + await(awaitAlias) + .atMost(10, TimeUnit.SECONDS) + .until(() -> CoAP.ResponseCode.CONTENT.equals(callbackCoap.getResponseCode()) && + callbackCoap.getObserve() != null && + expectedObserveForAttributesUpdate == callbackCoap.getObserve().intValue()); + validateUpdateProtoAttributesResponse(callbackCoap, expectedObserveForAttributesUpdate); + + int expectedObserveForAttributesDelete = callbackCoap.getObserve().intValue() + 1; doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=sharedJson", String.class); - latch.await(3, TimeUnit.SECONDS); - validateDeleteProtoAttributesResponse(callbackCoap, expectedObserveBeforeDeleteCnt); + awaitAlias = "await Proto Test Subscribe To Attributes Updates (deleted attributes)"; + await(awaitAlias) + .atMost(10, TimeUnit.SECONDS) + .until(() -> CoAP.ResponseCode.CONTENT.equals(callbackCoap.getResponseCode()) && + callbackCoap.getObserve() != null && + expectedObserveForAttributesDelete == callbackCoap.getObserve().intValue()); + validateDeleteProtoAttributesResponse(callbackCoap, expectedObserveForAttributesDelete); observeRelation.proactiveCancel(); assertTrue(observeRelation.isCanceled()); @@ -314,27 +339,18 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap assertTrue(actualSharedKeyValueProtos.containsAll(expectedSharedKeyValueProtos)); } - protected void validateUpdateAttributesJsonResponse(CoapTestCallback callback, String expectedResponse, int expectedObserveCnt) { + protected void validateUpdateAttributesJsonResponse(CoapTestCallback callback, String expectedResponse) { assertNotNull(callback.getPayloadBytes()); - assertNotNull(callback.getObserve()); - assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode()); - assertEquals(expectedObserveCnt, callback.getObserve().intValue()); String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8); assertEquals(JacksonUtil.toJsonNode(expectedResponse), JacksonUtil.toJsonNode(response)); } protected void validateEmptyCurrentStateAttributesProtoResponse(CoapTestCallback callback) throws InvalidProtocolBufferException { assertArrayEquals(EMPTY_PAYLOAD, callback.getPayloadBytes()); - assertNotNull(callback.getObserve()); - assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode()); - assertEquals(0, callback.getObserve().intValue()); } protected void validateCurrentStateAttributesProtoResponse(CoapTestCallback callback) throws InvalidProtocolBufferException { assertNotNull(callback.getPayloadBytes()); - assertNotNull(callback.getObserve()); - assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode()); - assertEquals(0, callback.getObserve().intValue()); TransportProtos.AttributeUpdateNotificationMsg.Builder expectedCurrentStateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder(); TransportProtos.TsKvProto tsKvProtoAttribute1 = getTsKvProto("sharedStr", "value", TransportProtos.KeyValueType.STRING_V); TransportProtos.TsKvProto tsKvProtoAttribute2 = getTsKvProto("sharedBool", "false", TransportProtos.KeyValueType.BOOLEAN_V); @@ -359,9 +375,6 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap protected void validateUpdateProtoAttributesResponse(CoapTestCallback callback, int expectedObserveCnt) throws InvalidProtocolBufferException { assertNotNull(callback.getPayloadBytes()); - assertNotNull(callback.getObserve()); - assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode()); - assertEquals(expectedObserveCnt, callback.getObserve().intValue()); TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder(); List tsKvProtoList = getTsKvProtoList("shared"); attributeUpdateNotificationMsgBuilder.addAllSharedUpdated(tsKvProtoList); @@ -378,9 +391,6 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap protected void validateDeleteProtoAttributesResponse(CoapTestCallback callback, int expectedObserveCnt) throws InvalidProtocolBufferException { assertNotNull(callback.getPayloadBytes()); - assertNotNull(callback.getObserve()); - assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode()); - assertEquals(expectedObserveCnt, callback.getObserve().intValue()); TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder(); attributeUpdateNotificationMsgBuilder.addSharedDeleted("sharedJson"); @@ -395,9 +405,10 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap Awaitility.await("awaitClientAfterCancelObserve") .pollInterval(10, TimeUnit.MILLISECONDS) .atMost(5, TimeUnit.SECONDS) - .until(()->{ + .until(() -> { log.trace("awaiting defaultTransportService.sessions is empty"); - return defaultTransportService.sessions.isEmpty();}); + return defaultTransportService.sessions.isEmpty(); + }); } private TransportProtos.GetAttributeResponseMsg getExpectedAttributeResponseMsg() { diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcIntegrationTest.java index beb4d3747c..ca5b40efef 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcIntegrationTest.java @@ -43,6 +43,7 @@ import org.thingsboard.server.transport.coap.CoapTestClient; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import static org.awaitility.Awaitility.await; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -75,14 +76,23 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC CoapTestCallback callbackCoap = new TestCoapCallbackForRPC(client, 1, true, protobuf); CoapObserveRelation observeRelation = client.getObserveRelation(callbackCoap); - callbackCoap.getLatch().await(3, TimeUnit.SECONDS); + String awaitAlias = "await One Way Rpc (client.getObserveRelation)"; + await(awaitAlias) + .atMost(10, TimeUnit.SECONDS) + .until(() -> CoAP.ResponseCode.VALID.equals(callbackCoap.getResponseCode()) && + callbackCoap.getObserve() != null && + 0 == callbackCoap.getObserve().intValue()); validateCurrentStateNotification(callbackCoap); - - CountDownLatch latch = new CountDownLatch(1); + int expectedObserveCountAfterGpioRequest = callbackCoap.getObserve().intValue() + 1; String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; String deviceId = savedDevice.getId().getId().toString(); String result = doPostAsync("/api/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk()); - latch.await(3, TimeUnit.SECONDS); + awaitAlias = "await One Way Rpc setGpio(method, params, value)"; + await(awaitAlias) + .atMost(10, TimeUnit.SECONDS) + .until(() -> CoAP.ResponseCode.CONTENT.equals(callbackCoap.getResponseCode()) && + callbackCoap.getObserve() != null && + expectedObserveCountAfterGpioRequest == callbackCoap.getObserve().intValue()); validateOneWayStateChangedNotification(callbackCoap, result); observeRelation.proactiveCancel(); @@ -94,23 +104,36 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC CoapTestCallback callbackCoap = new TestCoapCallbackForRPC(client, 1, false, protobuf); CoapObserveRelation observeRelation = client.getObserveRelation(callbackCoap); - callbackCoap.getLatch().await(3, TimeUnit.SECONDS); - + String awaitAlias = "await Two Way Rpc (client.getObserveRelation)"; + await(awaitAlias) + .atMost(10, TimeUnit.SECONDS) + .until(() -> CoAP.ResponseCode.VALID.equals(callbackCoap.getResponseCode()) && + callbackCoap.getObserve() != null && + 0 == callbackCoap.getObserve().intValue()); validateCurrentStateNotification(callbackCoap); String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}"; String deviceId = savedDevice.getId().getId().toString(); - + int expectedObserveCountAfterGpioRequest1 = callbackCoap.getObserve().intValue() + 1; String actualResult = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk()); - callbackCoap.getLatch().await(3, TimeUnit.SECONDS); - - validateTwoWayStateChangedNotification(callbackCoap, 1, expectedResponseResult, actualResult); - - CountDownLatch latch = new CountDownLatch(1); + awaitAlias = "await Two Way Rpc (setGpio(method, params, value) first"; + await(awaitAlias) + .atMost(10, TimeUnit.SECONDS) + .until(() -> CoAP.ResponseCode.CONTENT.equals(callbackCoap.getResponseCode()) && + callbackCoap.getObserve() != null && + expectedObserveCountAfterGpioRequest1 == callbackCoap.getObserve().intValue()); + validateTwoWayStateChangedNotification(callbackCoap, expectedResponseResult, actualResult); + + int expectedObserveCountAfterGpioRequest2 = callbackCoap.getObserve().intValue() + 1; actualResult = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk()); - callbackCoap.getLatch().await(3, TimeUnit.SECONDS); + awaitAlias = "await Two Way Rpc (setGpio(method, params, value) first"; + await(awaitAlias) + .atMost(10, TimeUnit.SECONDS) + .until(() -> CoAP.ResponseCode.CONTENT.equals(callbackCoap.getResponseCode()) && + callbackCoap.getObserve() != null && + expectedObserveCountAfterGpioRequest2 == callbackCoap.getObserve().intValue()); - validateTwoWayStateChangedNotification(callbackCoap, 2, expectedResponseResult, actualResult); + validateTwoWayStateChangedNotification(callbackCoap, expectedResponseResult, actualResult); observeRelation.proactiveCancel(); assertTrue(observeRelation.isCanceled()); @@ -184,25 +207,16 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC private void validateCurrentStateNotification(CoapTestCallback callback) { assertArrayEquals(EMPTY_PAYLOAD, callback.getPayloadBytes()); - assertNotNull(callback.getObserve()); - assertEquals(callback.getResponseCode(), CoAP.ResponseCode.VALID); - assertEquals(0, callback.getObserve().intValue()); } private void validateOneWayStateChangedNotification(CoapTestCallback callback, String result) { assertTrue(StringUtils.isEmpty(result)); assertNotNull(callback.getPayloadBytes()); - assertNotNull(callback.getObserve()); - assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode()); - assertEquals(1, callback.getObserve().intValue()); } - private void validateTwoWayStateChangedNotification(CoapTestCallback callback, int expectedObserveNumber, String expectedResult, String actualResult) { + private void validateTwoWayStateChangedNotification(CoapTestCallback callback, String expectedResult, String actualResult) { assertEquals(expectedResult, actualResult); assertNotNull(callback.getPayloadBytes()); - assertNotNull(callback.getObserve()); - assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode()); - assertEquals(expectedObserveNumber, callback.getObserve().intValue()); } protected class TestCoapCallbackForRPC extends CoapTestCallback { diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/rpc/CoapServerSideRpcJsonIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/rpc/CoapServerSideRpcJsonIntegrationTest.java index 96ab02325a..29fe4faf70 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/rpc/CoapServerSideRpcJsonIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/rpc/CoapServerSideRpcJsonIntegrationTest.java @@ -52,5 +52,4 @@ public class CoapServerSideRpcJsonIntegrationTest extends AbstractCoapServerSide public void testServerCoapTwoWayRpc() throws Exception { processTwoWayRpcTest("{\"value1\":\"A\",\"value2\":\"B\"}", false); } - } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java index e357b23afe..6547405337 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java @@ -196,7 +196,7 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M device.getId().getId().toString(); lwM2MTestClient.start(isStartLw); await(awaitAlias) - .atMost(1000, TimeUnit.MILLISECONDS) + .atMost(20, TimeUnit.SECONDS) .until(() -> finishState.equals(lwM2MTestClient.getClientState())); Assert.assertEquals(expectedStatuses, lwM2MTestClient.getClientStates()); } @@ -234,7 +234,7 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M String deviceId = device.getId().getId().toString(); lwM2MTestClient.start(true); await(awaitAlias) - .atMost(1000, TimeUnit.MILLISECONDS) + .atMost(20, TimeUnit.SECONDS) .until(() -> ON_REGISTRATION_SUCCESS.equals(lwM2MTestClient.getClientState())); Assert.assertEquals(expectedStatusesLwm2m, lwM2MTestClient.getClientStates()); @@ -246,7 +246,7 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M expectedStatusesBs.add(ON_DEREGISTRATION_STARTED); expectedStatusesBs.add(ON_DEREGISTRATION_SUCCESS); await(awaitAlias) - .atMost(1000, TimeUnit.MILLISECONDS) + .atMost(20, TimeUnit.SECONDS) .until(() -> ON_REGISTRATION_SUCCESS.equals(lwM2MTestClient.getClientState())); Assert.assertEquals(expectedStatusesBs, lwM2MTestClient.getClientStates()); } diff --git a/common/actor/pom.xml b/common/actor/pom.xml index a06fa00b55..a565ed9052 100644 --- a/common/actor/pom.xml +++ b/common/actor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.4.0-SNAPSHOT + 3.4.1-SNAPSHOT common org.thingsboard.common diff --git a/common/cache/pom.xml b/common/cache/pom.xml index b52984e0fc..d3f2c838c8 100644 --- a/common/cache/pom.xml +++ b/common/cache/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.4.0-SNAPSHOT + 3.4.1-SNAPSHOT common org.thingsboard.common diff --git a/common/cluster-api/pom.xml b/common/cluster-api/pom.xml index b531f26597..62175cae08 100644 --- a/common/cluster-api/pom.xml +++ b/common/cluster-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.4.0-SNAPSHOT + 3.4.1-SNAPSHOT common org.thingsboard.common diff --git a/common/cluster-api/src/main/proto/queue.proto b/common/cluster-api/src/main/proto/queue.proto index 324df48c2a..1714e109a3 100644 --- a/common/cluster-api/src/main/proto/queue.proto +++ b/common/cluster-api/src/main/proto/queue.proto @@ -273,7 +273,6 @@ message GetTenantRoutingInfoRequestMsg { } message GetTenantRoutingInfoResponseMsg { - bool isolatedTbCore = 1; bool isolatedTbRuleEngine = 2; } @@ -804,9 +803,8 @@ message EntityContentRequestMsg { message EntityContentResponseMsg { string data = 1; - string chunkedMsgId = 2; - int32 chunkIndex = 3; - int32 chunksCount = 4; + int32 chunkIndex = 2; + int32 chunksCount = 3; } message EntitiesContentRequestMsg { @@ -818,7 +816,8 @@ message EntitiesContentRequestMsg { message EntitiesContentResponseMsg { EntityContentResponseMsg item = 1; - int32 itemsCount = 2; + int32 itemIdx = 2; + int32 itemsCount = 3; } message VersionsDiffRequestMsg { diff --git a/common/coap-server/pom.xml b/common/coap-server/pom.xml index fad7c87420..5eba5c7175 100644 --- a/common/coap-server/pom.xml +++ b/common/coap-server/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.4.0-SNAPSHOT + 3.4.1-SNAPSHOT common org.thingsboard.common diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml index 931d7dc6d4..4b15effa44 100644 --- a/common/dao-api/pom.xml +++ b/common/dao-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.4.0-SNAPSHOT + 3.4.1-SNAPSHOT common org.thingsboard.common diff --git a/common/data/pom.xml b/common/data/pom.xml index 539dd0d9e3..75758021c8 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.4.0-SNAPSHOT + 3.4.1-SNAPSHOT common org.thingsboard.common diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/AdminSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/AdminSettings.java index d88fabc052..ab92f267f6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/AdminSettings.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/AdminSettings.java @@ -58,13 +58,13 @@ public class AdminSettings extends BaseData implements HasTenan return super.getId(); } - @ApiModelProperty(position = 2, value = "Timestamp of the settings creation, in milliseconds", example = "1609459200000", readOnly = true) + @ApiModelProperty(position = 2, value = "Timestamp of the settings creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Override public long getCreatedTime() { return super.getCreatedTime(); } - @ApiModelProperty(position = 3, value = "JSON object with Tenant Id.", readOnly = true) + @ApiModelProperty(position = 3, value = "JSON object with Tenant Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public TenantId getTenantId() { return tenantId; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java index 9ce638e667..bdd06606a6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java @@ -83,7 +83,7 @@ public class Customer extends ContactBased implements HasTenantId, E return super.getId(); } - @ApiModelProperty(position = 2, value = "Timestamp of the customer creation, in milliseconds", example = "1609459200000", readOnly = true) + @ApiModelProperty(position = 2, value = "Timestamp of the customer creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Override public long getCreatedTime() { return super.getCreatedTime(); @@ -159,7 +159,7 @@ public class Customer extends ContactBased implements HasTenantId, E @Override @JsonProperty(access = Access.READ_ONLY) - @ApiModelProperty(position = 4, value = "Name of the customer. Read-only, duplicated from title for backward compatibility", example = "Company A", readOnly = true) + @ApiModelProperty(position = 4, value = "Name of the customer. Read-only, duplicated from title for backward compatibility", example = "Company A", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public String getName() { return title; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java index 0a6d2260d2..1895b41639 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java @@ -71,13 +71,13 @@ public class DashboardInfo extends SearchTextBased implements HasNa return super.getId(); } - @ApiModelProperty(position = 2, value = "Timestamp of the dashboard creation, in milliseconds", example = "1609459200000", readOnly = true) + @ApiModelProperty(position = 2, value = "Timestamp of the dashboard creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Override public long getCreatedTime() { return super.getCreatedTime(); } - @ApiModelProperty(position = 3, value = "JSON object with Tenant Id. Tenant Id of the dashboard can't be changed.", readOnly = true) + @ApiModelProperty(position = 3, value = "JSON object with Tenant Id. Tenant Id of the dashboard can't be changed.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public TenantId getTenantId() { return tenantId; } @@ -95,7 +95,7 @@ public class DashboardInfo extends SearchTextBased implements HasNa this.title = title; } - @ApiModelProperty(position = 8, value = "Thumbnail picture for rendering of the dashboards in a grid view on mobile devices.", readOnly = true) + @ApiModelProperty(position = 8, value = "Thumbnail picture for rendering of the dashboards in a grid view on mobile devices.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public String getImage() { return image; } @@ -104,7 +104,7 @@ public class DashboardInfo extends SearchTextBased implements HasNa this.image = image; } - @ApiModelProperty(position = 5, value = "List of assigned customers with their info.", readOnly = true) + @ApiModelProperty(position = 5, value = "List of assigned customers with their info.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public Set getAssignedCustomers() { return assignedCustomers; } @@ -113,7 +113,7 @@ public class DashboardInfo extends SearchTextBased implements HasNa this.assignedCustomers = assignedCustomers; } - @ApiModelProperty(position = 6, value = "Hide dashboard from mobile devices. Useful if the dashboard is not designed for small screens.", readOnly = true) + @ApiModelProperty(position = 6, value = "Hide dashboard from mobile devices. Useful if the dashboard is not designed for small screens.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public boolean isMobileHide() { return mobileHide; } @@ -122,7 +122,7 @@ public class DashboardInfo extends SearchTextBased implements HasNa this.mobileHide = mobileHide; } - @ApiModelProperty(position = 7, value = "Order on mobile devices. Useful to adjust sorting of the dashboards for mobile applications", readOnly = true) + @ApiModelProperty(position = 7, value = "Order on mobile devices. Useful to adjust sorting of the dashboards for mobile applications", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public Integer getMobileOrder() { return mobileOrder; } @@ -180,7 +180,7 @@ public class DashboardInfo extends SearchTextBased implements HasNa } } - @ApiModelProperty(position = 4, value = "Same as title of the dashboard. Read-only field. Update the 'title' to change the 'name' of the dashboard.", readOnly = true) + @ApiModelProperty(position = 4, value = "Same as title of the dashboard. Read-only field. Update the 'title' to change the 'name' of the dashboard.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Override @JsonProperty(access = JsonProperty.Access.READ_ONLY) public String getName() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java index 678c68c115..4a6589d72c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java @@ -112,13 +112,13 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen return super.getId(); } - @ApiModelProperty(position = 2, value = "Timestamp of the device creation, in milliseconds", example = "1609459200000", readOnly = true) + @ApiModelProperty(position = 2, value = "Timestamp of the device creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Override public long getCreatedTime() { return super.getCreatedTime(); } - @ApiModelProperty(position = 3, value = "JSON object with Tenant Id. Use 'assignDeviceToTenant' to change the Tenant Id.", readOnly = true) + @ApiModelProperty(position = 3, value = "JSON object with Tenant Id. Use 'assignDeviceToTenant' to change the Tenant Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public TenantId getTenantId() { return tenantId; } @@ -127,7 +127,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen this.tenantId = tenantId; } - @ApiModelProperty(position = 4, value = "JSON object with Customer Id. Use 'assignDeviceToCustomer' to change the Customer Id.", readOnly = true) + @ApiModelProperty(position = 4, value = "JSON object with Customer Id. Use 'assignDeviceToCustomer' to change the Customer Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public CustomerId getCustomerId() { return customerId; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceInfo.java index a2e569d602..ccb9c29b6b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceInfo.java @@ -24,11 +24,11 @@ import org.thingsboard.server.common.data.id.DeviceId; @Data public class DeviceInfo extends Device { - @ApiModelProperty(position = 13, value = "Title of the Customer that owns the device.", readOnly = true) + @ApiModelProperty(position = 13, value = "Title of the Customer that owns the device.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String customerTitle; - @ApiModelProperty(position = 14, value = "Indicates special 'Public' Customer that is auto-generated to use the devices on public dashboards.", readOnly = true) + @ApiModelProperty(position = 14, value = "Indicates special 'Public' Customer that is auto-generated to use the devices on public dashboards.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private boolean customerIsPublic; - @ApiModelProperty(position = 15, value = "Name of the corresponding Device Profile.", readOnly = true) + @ApiModelProperty(position = 15, value = "Name of the corresponding Device Profile.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String deviceProfileName; public DeviceInfo() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java index e087ba0b7c..da8edc695e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java @@ -49,7 +49,7 @@ public class DeviceProfile extends SearchTextBased implements H private static final long serialVersionUID = 6998485460273302018L; - @ApiModelProperty(position = 3, value = "JSON object with Tenant Id that owns the profile.", readOnly = true) + @ApiModelProperty(position = 3, value = "JSON object with Tenant Id that owns the profile.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private TenantId tenantId; @NoXss @Length(fieldName = "name") @@ -74,12 +74,11 @@ public class DeviceProfile extends SearchTextBased implements H private RuleChainId defaultRuleChainId; @ApiModelProperty(position = 6, value = "Reference to the dashboard. Used in the mobile application to open the default dashboard when user navigates to device details.") private DashboardId defaultDashboardId; + @NoXss - @ApiModelProperty(position = 8, value = "Reference to the rule engine queue. " + + @ApiModelProperty(position = 8, value = "Rule engine queue name. " + "If present, the specified queue will be used to store all unprocessed messages related to device, including telemetry, attribute updates, etc. " + "Otherwise, the 'Main' queue will be used to store those messages.") - private QueueId defaultQueueId; - private String defaultQueueName; @Valid private transient DeviceProfileData profileData; @@ -113,7 +112,7 @@ public class DeviceProfile extends SearchTextBased implements H this.isDefault = deviceProfile.isDefault(); this.defaultRuleChainId = deviceProfile.getDefaultRuleChainId(); this.defaultDashboardId = deviceProfile.getDefaultDashboardId(); - this.defaultQueueId = deviceProfile.getDefaultQueueId(); + this.defaultQueueName = deviceProfile.getDefaultQueueName(); this.setProfileData(deviceProfile.getProfileData()); this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey(); this.firmwareId = deviceProfile.getFirmwareId(); @@ -130,7 +129,7 @@ public class DeviceProfile extends SearchTextBased implements H return super.getId(); } - @ApiModelProperty(position = 2, value = "Timestamp of the profile creation, in milliseconds", example = "1609459200000", readOnly = true) + @ApiModelProperty(position = 2, value = "Timestamp of the profile creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Override public long getCreatedTime() { return super.getCreatedTime(); @@ -174,13 +173,4 @@ public class DeviceProfile extends SearchTextBased implements H } } - @JsonIgnore - public String getDefaultQueueName() { - return defaultQueueName; - } - - @JsonProperty - public void setDefaultQueueName(String defaultQueueName) { - this.defaultQueueName = defaultQueueName; - } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java index a7e21b8a25..51129801e3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java @@ -87,7 +87,7 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo return getName() /*What the ...*/; } - @ApiModelProperty(position = 4, value = "JSON object with Customer Id. Use 'assignEntityViewToCustomer' to change the Customer Id.", readOnly = true) + @ApiModelProperty(position = 4, value = "JSON object with Customer Id. Use 'assignEntityViewToCustomer' to change the Customer Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Override public CustomerId getCustomerId() { return customerId; @@ -98,7 +98,7 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo return name; } - @ApiModelProperty(position = 3, value = "JSON object with Tenant Id.", readOnly = true) + @ApiModelProperty(position = 3, value = "JSON object with Tenant Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Override public TenantId getTenantId() { return tenantId; @@ -113,7 +113,7 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo return super.getId(); } - @ApiModelProperty(position = 2, value = "Timestamp of the Entity View creation, in milliseconds", example = "1609459200000", readOnly = true) + @ApiModelProperty(position = 2, value = "Timestamp of the Entity View creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Override public long getCreatedTime() { return super.getCreatedTime(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityViewInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityViewInfo.java index 4b8ac3f066..b9acde7af0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityViewInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityViewInfo.java @@ -22,9 +22,9 @@ import org.thingsboard.server.common.data.id.EntityViewId; @Data public class EntityViewInfo extends EntityView { - @ApiModelProperty(position = 12, value = "Title of the Customer that owns the entity view.", readOnly = true) + @ApiModelProperty(position = 12, value = "Title of the Customer that owns the entity view.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String customerTitle; - @ApiModelProperty(position = 13, value = "Indicates special 'Public' Customer that is auto-generated to use the entity view on public dashboards.", readOnly = true) + @ApiModelProperty(position = 13, value = "Indicates special 'Public' Customer that is auto-generated to use the entity view on public dashboards.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private boolean customerIsPublic; public EntityViewInfo() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Event.java b/common/data/src/main/java/org/thingsboard/server/common/data/Event.java index 6ff574917d..29ab33d075 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Event.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Event.java @@ -30,13 +30,13 @@ import org.thingsboard.server.common.data.id.TenantId; @ApiModel public class Event extends BaseData { - @ApiModelProperty(position = 1, value = "JSON object with Tenant Id.", readOnly = true) + @ApiModelProperty(position = 1, value = "JSON object with Tenant Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private TenantId tenantId; @ApiModelProperty(position = 2, value = "Event type", example = "STATS") private String type; @ApiModelProperty(position = 3, value = "string", example = "784f394c-42b6-435a-983c-b7beff2784f9") private String uid; - @ApiModelProperty(position = 4, value = "JSON object with Entity Id for which event is created.", readOnly = true) + @ApiModelProperty(position = 4, value = "JSON object with Entity Id for which event is created.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private EntityId entityId; @ApiModelProperty(position = 5, value = "Event body.", dataType = "com.fasterxml.jackson.databind.JsonNode") private transient JsonNode body; @@ -53,7 +53,7 @@ public class Event extends BaseData { super(event); } - @ApiModelProperty(position = 6, value = "Timestamp of the event creation, in milliseconds", example = "1609459200000", readOnly = true) + @ApiModelProperty(position = 6, value = "Timestamp of the event creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Override public long getCreatedTime() { return super.getCreatedTime(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ExportableEntity.java b/common/data/src/main/java/org/thingsboard/server/common/data/ExportableEntity.java index 64834c8b15..57541d02e2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ExportableEntity.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ExportableEntity.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data; +import io.swagger.annotations.ApiModelProperty; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; @@ -23,10 +24,13 @@ public interface ExportableEntity extends HasId, HasName void setId(I id); + @ApiModelProperty(position = 100, value = "JSON object with External Id from the VCS", accessMode = ApiModelProperty.AccessMode.READ_ONLY, hidden = true) I getExternalId(); + void setExternalId(I externalId); long getCreatedTime(); + void setCreatedTime(long createdTime); void setTenantId(TenantId tenantId); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackage.java b/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackage.java index 3a17aefab8..b708979006 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackage.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackage.java @@ -30,7 +30,7 @@ public class OtaPackage extends OtaPackageInfo { private static final long serialVersionUID = 3091601761339422546L; - @ApiModelProperty(position = 16, value = "OTA Package data.", readOnly = true) + @ApiModelProperty(position = 16, value = "OTA Package data.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private transient ByteBuffer data; public OtaPackage() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackageInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackageInfo.java index c860d06a4c..b31d0a6bc0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackageInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackageInfo.java @@ -40,44 +40,44 @@ public class OtaPackageInfo extends SearchTextBasedWithAdditionalInfo implements Has private static final long serialVersionUID = 7282664529021651736L; - @ApiModelProperty(position = 3, value = "JSON object with Tenant Id. Tenant Id of the resource can't be changed.", readOnly = true) + @ApiModelProperty(position = 3, value = "JSON object with Tenant Id. Tenant Id of the resource can't be changed.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private TenantId tenantId; @NoXss @Length(fieldName = "title") @ApiModelProperty(position = 4, value = "Resource title.", example = "BinaryAppDataContainer id=19 v1.0") private String title; - @ApiModelProperty(position = 5, value = "Resource type.", example = "LWM2M_MODEL", readOnly = true) + @ApiModelProperty(position = 5, value = "Resource type.", example = "LWM2M_MODEL", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private ResourceType resourceType; @NoXss @Length(fieldName = "resourceKey") - @ApiModelProperty(position = 6, value = "Resource key.", example = "19_1.0", readOnly = true) + @ApiModelProperty(position = 6, value = "Resource key.", example = "19_1.0", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String resourceKey; - @ApiModelProperty(position = 7, value = "Resource search text.", example = "19_1.0:binaryappdatacontainer", readOnly = true) + @ApiModelProperty(position = 7, value = "Resource search text.", example = "19_1.0:binaryappdatacontainer", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String searchText; public TbResourceInfo() { @@ -75,7 +75,7 @@ public class TbResourceInfo extends SearchTextBased implements Has return super.getId(); } - @ApiModelProperty(position = 2, value = "Timestamp of the resource creation, in milliseconds", example = "1609459200000", readOnly = true) + @ApiModelProperty(position = 2, value = "Timestamp of the resource creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Override public long getCreatedTime() { return super.getCreatedTime(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java b/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java index 5a55669646..8d5a9fe3fc 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java @@ -74,7 +74,7 @@ public class Tenant extends ContactBased implements HasTenantId { } @Override - @ApiModelProperty(position = 4, value = "Name of the tenant. Read-only, duplicated from title for backward compatibility", example = "Company A", readOnly = true) + @ApiModelProperty(position = 4, value = "Name of the tenant. Read-only, duplicated from title for backward compatibility", example = "Company A", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @JsonProperty(access = JsonProperty.Access.READ_ONLY) public String getName() { return title; @@ -110,7 +110,7 @@ public class Tenant extends ContactBased implements HasTenantId { return super.getId(); } - @ApiModelProperty(position = 2, value = "Timestamp of the tenant creation, in milliseconds", example = "1609459200000", readOnly = true) + @ApiModelProperty(position = 2, value = "Timestamp of the tenant creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Override public long getCreatedTime() { return super.getCreatedTime(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java b/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java index 4ebb903acb..89bd02344a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java @@ -53,13 +53,10 @@ public class TenantProfile extends SearchTextBased implements H private String description; @ApiModelProperty(position = 5, value = "Default Tenant profile to be used.", example = "true") private boolean isDefault; - @ApiModelProperty(position = 6, value = "If enabled, will push all messages related to this tenant and processed by core platform services into separate queue. " + - "Useful for complex microservices deployments, to isolate processing of the data for specific tenants", example = "true") - private boolean isolatedTbCore; - @ApiModelProperty(position = 7, value = "If enabled, will push all messages related to this tenant and processed by the rule engine into separate queue. " + + @ApiModelProperty(position = 6, value = "If enabled, will push all messages related to this tenant and processed by the rule engine into separate queue. " + "Useful for complex microservices deployments, to isolate processing of the data for specific tenants", example = "true") private boolean isolatedTbRuleEngine; - @ApiModelProperty(position = 8, value = "Complex JSON object that contains profile settings: queue configs, max devices, max assets, rate limits, etc.") + @ApiModelProperty(position = 7, value = "Complex JSON object that contains profile settings: queue configs, max devices, max assets, rate limits, etc.") private transient TenantProfileData profileData; @JsonIgnore private byte[] profileDataBytes; @@ -77,7 +74,6 @@ public class TenantProfile extends SearchTextBased implements H this.name = tenantProfile.getName(); this.description = tenantProfile.getDescription(); this.isDefault = tenantProfile.isDefault(); - this.isolatedTbCore = tenantProfile.isIsolatedTbCore(); this.isolatedTbRuleEngine = tenantProfile.isIsolatedTbRuleEngine(); this.setProfileData(tenantProfile.getProfileData()); } @@ -91,7 +87,7 @@ public class TenantProfile extends SearchTextBased implements H return super.getId(); } - @ApiModelProperty(position = 2, value = "Timestamp of the tenant profile creation, in milliseconds", example = "1609459200000", readOnly = true) + @ApiModelProperty(position = 2, value = "Timestamp of the tenant profile creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Override public long getCreatedTime() { return super.getCreatedTime(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/User.java b/common/data/src/main/java/org/thingsboard/server/common/data/User.java index c207135f6d..60d8d7eca9 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/User.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/User.java @@ -74,13 +74,13 @@ public class User extends SearchTextBasedWithAdditionalInfo implements H return super.getId(); } - @ApiModelProperty(position = 2, value = "Timestamp of the user creation, in milliseconds", example = "1609459200000", readOnly = true) + @ApiModelProperty(position = 2, value = "Timestamp of the user creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Override public long getCreatedTime() { return super.getCreatedTime(); } - @ApiModelProperty(position = 3, value = "JSON object with the Tenant Id.", readOnly = true) + @ApiModelProperty(position = 3, value = "JSON object with the Tenant Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public TenantId getTenantId() { return tenantId; } @@ -89,7 +89,7 @@ public class User extends SearchTextBasedWithAdditionalInfo implements H this.tenantId = tenantId; } - @ApiModelProperty(position = 4, value = "JSON object with the Customer Id.", readOnly = true) + @ApiModelProperty(position = 4, value = "JSON object with the Customer Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public CustomerId getCustomerId() { return customerId; } @@ -107,7 +107,7 @@ public class User extends SearchTextBasedWithAdditionalInfo implements H this.email = email; } - @ApiModelProperty(position = 6, readOnly = true, value = "Duplicates the email of the user, readonly", example = "user@example.com") + @ApiModelProperty(position = 6, accessMode = ApiModelProperty.AccessMode.READ_ONLY, value = "Duplicates the email of the user, readonly", example = "user@example.com") @Override @JsonProperty(access = JsonProperty.Access.READ_ONLY) public String getName() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java index 7587d687dd..f13c84bc82 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java @@ -43,10 +43,10 @@ import java.util.List; @AllArgsConstructor public class Alarm extends BaseData implements HasName, HasTenantId, HasCustomerId { - @ApiModelProperty(position = 3, value = "JSON object with Tenant Id", readOnly = true) + @ApiModelProperty(position = 3, value = "JSON object with Tenant Id", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private TenantId tenantId; - @ApiModelProperty(position = 4, value = "JSON object with Customer Id", readOnly = true) + @ApiModelProperty(position = 4, value = "JSON object with Customer Id", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private CustomerId customerId; @ApiModelProperty(position = 6, required = true, value = "representing type of the Alarm", example = "High Temperature Alarm") @@ -124,7 +124,7 @@ public class Alarm extends BaseData implements HasName, HasTenantId, Ha } - @ApiModelProperty(position = 2, value = "Timestamp of the alarm creation, in milliseconds", example = "1634058704567", readOnly = true) + @ApiModelProperty(position = 2, value = "Timestamp of the alarm creation, in milliseconds", example = "1634058704567", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Override public long getCreatedTime() { return super.getCreatedTime(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java index da6d9615a1..f4ad335a43 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java @@ -92,13 +92,13 @@ public class Asset extends SearchTextBasedWithAdditionalInfo implements return super.getId(); } - @ApiModelProperty(position = 2, value = "Timestamp of the asset creation, in milliseconds", example = "1609459200000", readOnly = true) + @ApiModelProperty(position = 2, value = "Timestamp of the asset creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Override public long getCreatedTime() { return super.getCreatedTime(); } - @ApiModelProperty(position = 3, value = "JSON object with Tenant Id.", readOnly = true) + @ApiModelProperty(position = 3, value = "JSON object with Tenant Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public TenantId getTenantId() { return tenantId; } @@ -107,7 +107,7 @@ public class Asset extends SearchTextBasedWithAdditionalInfo implements this.tenantId = tenantId; } - @ApiModelProperty(position = 4, value = "JSON object with Customer Id. Use 'assignAssetToCustomer' to change the Customer Id.", readOnly = true) + @ApiModelProperty(position = 4, value = "JSON object with Customer Id. Use 'assignAssetToCustomer' to change the Customer Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public CustomerId getCustomerId() { return customerId; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetInfo.java index 4b962aa811..a6a532cbe6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetInfo.java @@ -24,9 +24,9 @@ import org.thingsboard.server.common.data.id.AssetId; @Data public class AssetInfo extends Asset { - @ApiModelProperty(position = 9, value = "Title of the Customer that owns the asset.", readOnly = true) + @ApiModelProperty(position = 9, value = "Title of the Customer that owns the asset.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String customerTitle; - @ApiModelProperty(position = 10, value = "Indicates special 'Public' Customer that is auto-generated to use the assets on public dashboards.", readOnly = true) + @ApiModelProperty(position = 10, value = "Indicates special 'Public' Customer that is auto-generated to use the assets on public dashboards.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private boolean customerIsPublic; public AssetInfo() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/audit/AuditLog.java b/common/data/src/main/java/org/thingsboard/server/common/data/audit/AuditLog.java index 01f2336197..e87a1a6380 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/audit/AuditLog.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/audit/AuditLog.java @@ -28,25 +28,25 @@ import org.thingsboard.server.common.data.id.*; @Data public class AuditLog extends BaseData { - @ApiModelProperty(position = 3, value = "JSON object with Tenant Id", readOnly = true) + @ApiModelProperty(position = 3, value = "JSON object with Tenant Id", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private TenantId tenantId; - @ApiModelProperty(position = 4, value = "JSON object with Customer Id", readOnly = true) + @ApiModelProperty(position = 4, value = "JSON object with Customer Id", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private CustomerId customerId; - @ApiModelProperty(position = 5, value = "JSON object with Entity id", readOnly = true) + @ApiModelProperty(position = 5, value = "JSON object with Entity id", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private EntityId entityId; - @ApiModelProperty(position = 6, value = "Name of the logged entity", example = "Thermometer", readOnly = true) + @ApiModelProperty(position = 6, value = "Name of the logged entity", example = "Thermometer", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String entityName; - @ApiModelProperty(position = 7, value = "JSON object with User id.", readOnly = true) + @ApiModelProperty(position = 7, value = "JSON object with User id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private UserId userId; - @ApiModelProperty(position = 8, value = "Unique user name(email) of the user that performed some action on logged entity", example = "tenant@thingsboard.org", readOnly = true) + @ApiModelProperty(position = 8, value = "Unique user name(email) of the user that performed some action on logged entity", example = "tenant@thingsboard.org", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String userName; - @ApiModelProperty(position = 9, value = "String represented Action type", example = "ADDED", readOnly = true) + @ApiModelProperty(position = 9, value = "String represented Action type", example = "ADDED", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private ActionType actionType; - @ApiModelProperty(position = 10, value = "JsonNode represented action data", readOnly = true) + @ApiModelProperty(position = 10, value = "JsonNode represented action data", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private JsonNode actionData; - @ApiModelProperty(position = 11, value = "String represented Action status", example = "SUCCESS", allowableValues = "SUCCESS,FAILURE", readOnly = true) + @ApiModelProperty(position = 11, value = "String represented Action status", example = "SUCCESS", allowableValues = "SUCCESS,FAILURE", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private ActionStatus actionStatus; - @ApiModelProperty(position = 12, value = "Failure action details info. An empty string in case of action status type 'SUCCESS', otherwise includes stack trace of the caused exception.", readOnly = true) + @ApiModelProperty(position = 12, value = "Failure action details info. An empty string in case of action status type 'SUCCESS', otherwise includes stack trace of the caused exception.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String actionFailureDetails; public AuditLog() { @@ -71,7 +71,7 @@ public class AuditLog extends BaseData { this.actionFailureDetails = auditLog.getActionFailureDetails(); } - @ApiModelProperty(position = 2, value = "Timestamp of the auditLog creation, in milliseconds", example = "1609459200000", readOnly = true) + @ApiModelProperty(position = 2, value = "Timestamp of the auditLog creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Override public long getCreatedTime() { return super.getCreatedTime(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfig.java index 8232f6670f..2fc2b84586 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfig.java @@ -25,44 +25,44 @@ public class LwM2MServerSecurityConfig { @ApiModelProperty(position = 1, value = "Server short Id. Used as link to associate server Object Instance. This identifier uniquely identifies each LwM2M Server configured for the LwM2M Client. " + "This Resource MUST be set when the Bootstrap-Server Resource has a value of 'false'. " + - "The values ID:0 and ID:65535 values MUST NOT be used for identifying the LwM2M Server.", example = "123", readOnly = true) + "The values ID:0 and ID:65535 values MUST NOT be used for identifying the LwM2M Server.", example = "123", accessMode = ApiModelProperty.AccessMode.READ_ONLY) protected Integer shortServerId = 123; /** Security -> ObjectId = 0 'LWM2M Security' */ @ApiModelProperty(position = 2, value = "Is Bootstrap Server or Lwm2m Server. " + "The LwM2M Client MAY be configured to use one or more LwM2M Server Account(s). " + "The LwM2M Client MUST have at most one LwM2M Bootstrap-Server Account. " + - "(*) The LwM2M client MUST have at least one LwM2M server account after completing the boot sequence specified.", example = "true or false", readOnly = true) + "(*) The LwM2M client MUST have at least one LwM2M server account after completing the boot sequence specified.", example = "true or false", accessMode = ApiModelProperty.AccessMode.READ_ONLY) protected boolean bootstrapServerIs = false; - @ApiModelProperty(position = 3, value = "Host for 'No Security' mode", example = "0.0.0.0", readOnly = true) + @ApiModelProperty(position = 3, value = "Host for 'No Security' mode", example = "0.0.0.0", accessMode = ApiModelProperty.AccessMode.READ_ONLY) protected String host; - @ApiModelProperty(position = 4, value = "Port for Lwm2m Server: 'No Security' mode: Lwm2m Server or Bootstrap Server", example = "'5685' or '5687'", readOnly = true) + @ApiModelProperty(position = 4, value = "Port for Lwm2m Server: 'No Security' mode: Lwm2m Server or Bootstrap Server", example = "'5685' or '5687'", accessMode = ApiModelProperty.AccessMode.READ_ONLY) protected Integer port; - @ApiModelProperty(position = 7, value = "Client Hold Off Time. The number of seconds to wait before initiating a Client Initiated Bootstrap once the LwM2M Client has determined it should initiate this bootstrap mode. (This information is relevant for use with a Bootstrap-Server only.)", example = "1", readOnly = true) + @ApiModelProperty(position = 7, value = "Client Hold Off Time. The number of seconds to wait before initiating a Client Initiated Bootstrap once the LwM2M Client has determined it should initiate this bootstrap mode. (This information is relevant for use with a Bootstrap-Server only.)", example = "1", accessMode = ApiModelProperty.AccessMode.READ_ONLY) protected Integer clientHoldOffTime = 1; @ApiModelProperty(position = 8, value = "Server Public Key for 'Security' mode (DTLS): RPK or X509. Format: base64 encoded", example = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAZ0pSaGKHk/GrDaUDnQZpeEdGwX7m3Ws+U/kiVat\n" + - "+44sgk3c8g0LotfMpLlZJPhPwJ6ipXV+O1r7IZUjBs3LNA==", readOnly = true) + "+44sgk3c8g0LotfMpLlZJPhPwJ6ipXV+O1r7IZUjBs3LNA==", accessMode = ApiModelProperty.AccessMode.READ_ONLY) protected String serverPublicKey; @ApiModelProperty(position = 9, value = "Server Public Key for 'Security' mode (DTLS): X509. Format: base64 encoded", example = "MMIICODCCAd6gAwIBAgIUI88U1zowOdrxDK/dOV+36gJxI2MwCgYIKoZIzj0EAwIwejELMAkGA1UEBhMCVUs\n" + "xEjAQBgNVBAgTCUt5aXYgY2l0eTENMAsGA1UEBxMES3lpdjEUMBIGA1UEChMLVGhpbmdzYm9hcmQxFzAVBgNVBAsMDkRFVkVMT1BFUl9URVNUMRkwFwYDVQQDDBBpbnRlcm1lZGlhdGVfY2EwMB4XDTIyMDEwOTEzMDMwMFoXDTI3MDEwODEzMDMwMFowFDESMBAGA1UEAxM\n" + "JbG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUO3vBo/JTv0eooY7XHiKAIVDoWKFqtrU7C6q8AIKqpLcqhCdW+haFeBOH3PjY6EwaWkY04Bir4oanU0s7tz2uKOBpzCBpDAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/\n" + "BAIwADAdBgNVHQ4EFgQUEjc3Q4a0TxzP/3x3EV4fHxYUg0YwHwYDVR0jBBgwFoAUuSquGycMU6Q0SYNcbtSkSD3TfH0wLwYDVR0RBCgwJoIVbG9jYWxob3N0LmxvY2FsZG9tYWlugglsb2NhbGhvc3SCAiAtMAoGCCqGSM49BAMCA0gAMEUCIQD7dbZObyUaoDiNbX+9fUNp\n" + - "AWrD7N7XuJUwZ9FcN75R3gIgb2RNjDkHoyUyF1YajwkBk+7XmIXNClmizNJigj908mw=", readOnly = true) + "AWrD7N7XuJUwZ9FcN75R3gIgb2RNjDkHoyUyF1YajwkBk+7XmIXNClmizNJigj908mw=", accessMode = ApiModelProperty.AccessMode.READ_ONLY) protected String serverCertificate; - @ApiModelProperty(position = 10, value = "Bootstrap Server Account Timeout (If the value is set to 0, or if this resource is not instantiated, the Bootstrap-Server Account lifetime is infinite.)", example = "0", readOnly = true) + @ApiModelProperty(position = 10, value = "Bootstrap Server Account Timeout (If the value is set to 0, or if this resource is not instantiated, the Bootstrap-Server Account lifetime is infinite.)", example = "0", accessMode = ApiModelProperty.AccessMode.READ_ONLY) Integer bootstrapServerAccountTimeout = 0; /** Config -> ObjectId = 1 'LwM2M Server' */ - @ApiModelProperty(position = 11, value = "Specify the lifetime of the registration in seconds.", example = "300", readOnly = true) + @ApiModelProperty(position = 11, value = "Specify the lifetime of the registration in seconds.", example = "300", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private Integer lifetime = 300; @ApiModelProperty(position = 12, value = "The default value the LwM2M Client should use for the Minimum Period of an Observation in the absence of this parameter being included in an Observation. " + - "If this Resource doesn’t exist, the default value is 0.", example = "1", readOnly = true) + "If this Resource doesn’t exist, the default value is 0.", example = "1", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private Integer defaultMinPeriod = 1; /** ResourceID=6 'Notification Storing When Disabled or Offline' */ @ApiModelProperty(position = 13, value = "If true, the LwM2M Client stores “Notify” operations to the LwM2M Server while the LwM2M Server account is disabled or the LwM2M Client is offline. After the LwM2M Server account is enabled or the LwM2M Client is online, the LwM2M Client reports the stored “Notify” operations to the Server. " + "If false, the LwM2M Client discards all the “Notify” operations or temporarily disables the Observe function while the LwM2M Server is disabled or the LwM2M Client is offline. " + - "The default value is true.", example = "true", readOnly = true) + "The default value is true.", example = "true", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private boolean notifIfDisabled = true; @ApiModelProperty(position = 14, value = "This Resource defines the transport binding configured for the LwM2M Client. " + - "If the LwM2M Client supports the binding specified in this Resource, the LwM2M Client MUST use that transport for the Current Binding Mode.", example = "U", readOnly = true) + "If the LwM2M Client supports the binding specified in this Resource, the LwM2M Client MUST use that transport for the Current Binding Mode.", example = "U", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String binding = "U"; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfigDefault.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfigDefault.java index ce2686f086..948a492e42 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfigDefault.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfigDefault.java @@ -22,8 +22,8 @@ import lombok.Data; @ApiModel @Data public class LwM2MServerSecurityConfigDefault extends LwM2MServerSecurityConfig { - @ApiModelProperty(position = 5, value = "Host for 'Security' mode (DTLS)", example = "0.0.0.0", readOnly = true) + @ApiModelProperty(position = 5, value = "Host for 'Security' mode (DTLS)", example = "0.0.0.0", accessMode = ApiModelProperty.AccessMode.READ_ONLY) protected String securityHost; - @ApiModelProperty(position = 6, value = "Port for 'Security' mode (DTLS): Lwm2m Server or Bootstrap Server", example = "5686 or 5688", readOnly = true) + @ApiModelProperty(position = 6, value = "Port for 'Security' mode (DTLS): Lwm2m Server or Bootstrap Server", example = "5686 or 5688", accessMode = ApiModelProperty.AccessMode.READ_ONLY) protected Integer securityPort; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java b/common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java index 4a4cd1afc4..58c23d0877 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java @@ -98,25 +98,25 @@ public class Edge extends SearchTextBasedWithAdditionalInfo implements H return super.getId(); } - @ApiModelProperty(position = 2, value = "Timestamp of the edge creation, in milliseconds", example = "1609459200000", readOnly = true) + @ApiModelProperty(position = 2, value = "Timestamp of the edge creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Override public long getCreatedTime() { return super.getCreatedTime(); } - @ApiModelProperty(position = 3, value = "JSON object with Tenant Id. Use 'assignDeviceToTenant' to change the Tenant Id.", readOnly = true) + @ApiModelProperty(position = 3, value = "JSON object with Tenant Id. Use 'assignDeviceToTenant' to change the Tenant Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Override public TenantId getTenantId() { return this.tenantId; } - @ApiModelProperty(position = 4, value = "JSON object with Customer Id. Use 'assignEdgeToCustomer' to change the Customer Id.", readOnly = true) + @ApiModelProperty(position = 4, value = "JSON object with Customer Id. Use 'assignEdgeToCustomer' to change the Customer Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Override public CustomerId getCustomerId() { return this.customerId; } - @ApiModelProperty(position = 5, value = "JSON object with Root Rule Chain Id. Use 'setEdgeRootRuleChain' to change the Root Rule Chain Id.", readOnly = true) + @ApiModelProperty(position = 5, value = "JSON object with Root Rule Chain Id. Use 'setEdgeRootRuleChain' to change the Root Rule Chain Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public RuleChainId getRootRuleChainId() { return this.rootRuleChainId; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/page/PageData.java b/common/data/src/main/java/org/thingsboard/server/common/data/page/PageData.java index 02f6683989..511e9fe170 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/page/PageData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/page/PageData.java @@ -48,22 +48,22 @@ public class PageData { this.hasNext = hasNext; } - @ApiModelProperty(position = 1, value = "Array of the entities", readOnly = true) + @ApiModelProperty(position = 1, value = "Array of the entities", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public List getData() { return data; } - @ApiModelProperty(position = 2, value = "Total number of available pages. Calculated based on the 'pageSize' request parameter and total number of entities that match search criteria", readOnly = true) + @ApiModelProperty(position = 2, value = "Total number of available pages. Calculated based on the 'pageSize' request parameter and total number of entities that match search criteria", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public int getTotalPages() { return totalPages; } - @ApiModelProperty(position = 3, value = "Total number of elements in all available pages", readOnly = true) + @ApiModelProperty(position = 3, value = "Total number of elements in all available pages", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public long getTotalElements() { return totalElements; } - @ApiModelProperty(position = 4, value = "'false' value indicates the end of the result set", readOnly = true) + @ApiModelProperty(position = 4, value = "'false' value indicates the end of the result set", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @JsonProperty("hasNext") public boolean hasNext() { return hasNext; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentDescriptor.java b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentDescriptor.java index cdc5d55154..81708ae101 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentDescriptor.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentDescriptor.java @@ -32,19 +32,19 @@ public class ComponentDescriptor extends SearchTextBased private static final long serialVersionUID = 1L; - @ApiModelProperty(position = 3, value = "Type of the Rule Node", readOnly = true) + @ApiModelProperty(position = 3, value = "Type of the Rule Node", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Getter @Setter private ComponentType type; - @ApiModelProperty(position = 4, value = "Scope of the Rule Node. Always set to 'TENANT', since no rule chains on the 'SYSTEM' level yet.", readOnly = true, allowableValues = "TENANT", example = "TENANT") + @ApiModelProperty(position = 4, value = "Scope of the Rule Node. Always set to 'TENANT', since no rule chains on the 'SYSTEM' level yet.", accessMode = ApiModelProperty.AccessMode.READ_ONLY, allowableValues = "TENANT", example = "TENANT") @Getter @Setter private ComponentScope scope; @Length(fieldName = "name") - @ApiModelProperty(position = 5, value = "Name of the Rule Node. Taken from the @RuleNode annotation.", readOnly = true, example = "Custom Rule Node") + @ApiModelProperty(position = 5, value = "Name of the Rule Node. Taken from the @RuleNode annotation.", accessMode = ApiModelProperty.AccessMode.READ_ONLY, example = "Custom Rule Node") @Getter @Setter private String name; - @ApiModelProperty(position = 6, value = "Full name of the Java class that implements the Rule Engine Node interface.", readOnly = true, example = "com.mycompany.CustomRuleNode") + @ApiModelProperty(position = 6, value = "Full name of the Java class that implements the Rule Engine Node interface.", accessMode = ApiModelProperty.AccessMode.READ_ONLY, example = "com.mycompany.CustomRuleNode") @Getter @Setter private String clazz; - @ApiModelProperty(position = 7, value = "Complex JSON object that represents the Rule Node configuration.", readOnly = true) + @ApiModelProperty(position = 7, value = "Complex JSON object that represents the Rule Node configuration.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Getter @Setter private transient JsonNode configurationDescriptor; @Length(fieldName = "actions") - @ApiModelProperty(position = 8, value = "Rule Node Actions. Deprecated. Always null.", readOnly = true) + @ApiModelProperty(position = 8, value = "Rule Node Actions. Deprecated. Always null.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Getter @Setter private String actions; public ComponentDescriptor() { @@ -74,7 +74,7 @@ public class ComponentDescriptor extends SearchTextBased return super.getId(); } - @ApiModelProperty(position = 2, value = "Timestamp of the descriptor creation, in milliseconds", example = "1609459200000", readOnly = true) + @ApiModelProperty(position = 2, value = "Timestamp of the descriptor creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Override public long getCreatedTime() { return super.getCreatedTime(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java b/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java index d57b1aa9d7..8c7f2291cc 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java @@ -73,7 +73,7 @@ public class EntityRelation implements Serializable { this.additionalInfo = entityRelation.getAdditionalInfo(); } - @ApiModelProperty(position = 1, value = "JSON object with [from] Entity Id.", readOnly = true) + @ApiModelProperty(position = 1, value = "JSON object with [from] Entity Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public EntityId getFrom() { return from; } @@ -82,7 +82,7 @@ public class EntityRelation implements Serializable { this.from = from; } - @ApiModelProperty(position = 2, value = "JSON object with [to] Entity Id.", readOnly = true) + @ApiModelProperty(position = 2, value = "JSON object with [to] Entity Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public EntityId getTo() { return to; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelationInfo.java index 42f57b57a2..95714351e1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelationInfo.java @@ -32,7 +32,7 @@ public class EntityRelationInfo extends EntityRelation { super(entityRelation); } - @ApiModelProperty(position = 6, value = "Name of the entity for [from] direction.", readOnly = true, example = "A4B72CCDFF33") + @ApiModelProperty(position = 6, value = "Name of the entity for [from] direction.", accessMode = ApiModelProperty.AccessMode.READ_ONLY, example = "A4B72CCDFF33") public String getFromName() { return fromName; } @@ -41,7 +41,7 @@ public class EntityRelationInfo extends EntityRelation { this.fromName = fromName; } - @ApiModelProperty(position = 7, value = "Name of the entity for [to] direction.", readOnly = true, example = "A4B72CCDFF35") + @ApiModelProperty(position = 7, value = "Name of the entity for [to] direction.", accessMode = ApiModelProperty.AccessMode.READ_ONLY, example = "A4B72CCDFF35") public String getToName() { return toName; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rpc/Rpc.java b/common/data/src/main/java/org/thingsboard/server/common/data/rpc/Rpc.java index ee2ea6d830..7703c1aec2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rpc/Rpc.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rpc/Rpc.java @@ -31,19 +31,19 @@ import org.thingsboard.server.common.data.id.TenantId; @EqualsAndHashCode(callSuper = true) public class Rpc extends BaseData implements HasTenantId { - @ApiModelProperty(position = 3, value = "JSON object with Tenant Id.", readOnly = true) + @ApiModelProperty(position = 3, value = "JSON object with Tenant Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private TenantId tenantId; - @ApiModelProperty(position = 4, value = "JSON object with Device Id.", readOnly = true) + @ApiModelProperty(position = 4, value = "JSON object with Device Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private DeviceId deviceId; - @ApiModelProperty(position = 5, value = "Expiration time of the request.", readOnly = true) + @ApiModelProperty(position = 5, value = "Expiration time of the request.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private long expirationTime; - @ApiModelProperty(position = 6, value = "The request body that will be used to send message to device.", readOnly = true) + @ApiModelProperty(position = 6, value = "The request body that will be used to send message to device.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private JsonNode request; - @ApiModelProperty(position = 7, value = "The response from the device.", readOnly = true) + @ApiModelProperty(position = 7, value = "The response from the device.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private JsonNode response; - @ApiModelProperty(position = 8, value = "The current status of the RPC call.", readOnly = true) + @ApiModelProperty(position = 8, value = "The current status of the RPC call.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private RpcStatus status; - @ApiModelProperty(position = 9, value = "Additional info used in the rule engine to process the updates to the RPC state.", readOnly = true) + @ApiModelProperty(position = 9, value = "Additional info used in the rule engine to process the updates to the RPC state.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private JsonNode additionalInfo; public Rpc() { @@ -71,7 +71,7 @@ public class Rpc extends BaseData implements HasTenantId { return super.getId(); } - @ApiModelProperty(position = 2, value = "Timestamp of the rpc creation, in milliseconds", example = "1609459200000", readOnly = true) + @ApiModelProperty(position = 2, value = "Timestamp of the rpc creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Override public long getCreatedTime() { return super.getCreatedTime(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java index eca9e331cd..a8ba432ceb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java @@ -40,7 +40,7 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo im private static final long serialVersionUID = -5656679015121935465L; - @ApiModelProperty(position = 3, required = true, value = "JSON object with Tenant Id.", readOnly = true) + @ApiModelProperty(position = 3, required = true, value = "JSON object with Tenant Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private TenantId tenantId; @NoXss @Length(fieldName = "name") @@ -100,7 +100,7 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo im return super.getId(); } - @ApiModelProperty(position = 2, value = "Timestamp of the rule chain creation, in milliseconds", example = "1609459200000", readOnly = true) + @ApiModelProperty(position = 2, value = "Timestamp of the rule chain creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Override public long getCreatedTime() { return super.getCreatedTime(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainData.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainData.java index 93fd690d6d..885226caba 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainData.java @@ -25,8 +25,8 @@ import java.util.List; @Data public class RuleChainData { - @ApiModelProperty(position = 1, required = true, value = "List of the Rule Chain objects.", readOnly = true) + @ApiModelProperty(position = 1, required = true, value = "List of the Rule Chain objects.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) List ruleChains; - @ApiModelProperty(position = 2, required = true, value = "List of the Rule Chain metadata objects.", readOnly = true) + @ApiModelProperty(position = 2, required = true, value = "List of the Rule Chain metadata objects.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) List metadata; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainMetaData.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainMetaData.java index 7bd19e2441..fcb88924da 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainMetaData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainMetaData.java @@ -33,7 +33,7 @@ import java.util.Map; @Data public class RuleChainMetaData { - @ApiModelProperty(position = 1, required = true, value = "JSON object with Rule Chain Id.", readOnly = true) + @ApiModelProperty(position = 1, required = true, value = "JSON object with Rule Chain Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private RuleChainId ruleChainId; @ApiModelProperty(position = 2, required = true, value = "Index of the first rule node in the 'nodes' list") diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java index d9dce57f46..c9bf8b462c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java @@ -37,7 +37,7 @@ public class RuleNode extends SearchTextBasedWithAdditionalInfo impl private static final long serialVersionUID = -5656679015121235465L; - @ApiModelProperty(position = 3, value = "JSON object with the Rule Chain Id. ", readOnly = true) + @ApiModelProperty(position = 3, value = "JSON object with the Rule Chain Id. ", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private RuleChainId ruleChainId; @Length(fieldName = "type") @ApiModelProperty(position = 4, value = "Full Java Class Name of the rule node implementation. ", example = "com.mycompany.iot.rule.engine.ProcessingNode") @@ -100,7 +100,7 @@ public class RuleNode extends SearchTextBasedWithAdditionalInfo impl return super.getId(); } - @ApiModelProperty(position = 2, value = "Timestamp of the rule node creation, in milliseconds", example = "1609459200000", readOnly = true) + @ApiModelProperty(position = 2, value = "Timestamp of the rule node creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Override public long getCreatedTime() { return super.getCreatedTime(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentials.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentials.java index ed797eec70..e5af27776c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentials.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentials.java @@ -48,7 +48,7 @@ public class DeviceCredentials extends BaseData implements this.credentialsValue = deviceCredentials.getCredentialsValue(); } - @ApiModelProperty(position = 1, required = true, readOnly = true, value = "The Id is automatically generated during device creation. " + + @ApiModelProperty(position = 1, required = true, accessMode = ApiModelProperty.AccessMode.READ_ONLY, value = "The Id is automatically generated during device creation. " + "Use 'getDeviceCredentialsByDeviceId' to obtain the id based on device id. " + "Use 'updateDeviceCredentials' to update device credentials. ", example = "784f394c-42b6-435a-983c-b7beff2784f9") @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/widget/BaseWidgetType.java b/common/data/src/main/java/org/thingsboard/server/common/data/widget/BaseWidgetType.java index 0e88a6535d..be5014a4cd 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/widget/BaseWidgetType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/widget/BaseWidgetType.java @@ -29,19 +29,19 @@ public class BaseWidgetType extends BaseData implements HasTenantI private static final long serialVersionUID = 8388684344603660756L; - @ApiModelProperty(position = 3, value = "JSON object with Tenant Id.", readOnly = true) + @ApiModelProperty(position = 3, value = "JSON object with Tenant Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private TenantId tenantId; @NoXss @Length(fieldName = "bundleAlias") - @ApiModelProperty(position = 4, value = "Reference to widget bundle", readOnly = true) + @ApiModelProperty(position = 4, value = "Reference to widget bundle", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String bundleAlias; @NoXss @Length(fieldName = "alias") - @ApiModelProperty(position = 5, value = "Unique alias that is used in dashboards as a reference widget type", readOnly = true) + @ApiModelProperty(position = 5, value = "Unique alias that is used in dashboards as a reference widget type", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String alias; @NoXss @Length(fieldName = "name") - @ApiModelProperty(position = 6, value = "Widget name used in search and UI", readOnly = true) + @ApiModelProperty(position = 6, value = "Widget name used in search and UI", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String name; public BaseWidgetType() { @@ -69,7 +69,7 @@ public class BaseWidgetType extends BaseData implements HasTenantI return super.getId(); } - @ApiModelProperty(position = 2, value = "Timestamp of the Widget Type creation, in milliseconds", example = "1609459200000", readOnly = true) + @ApiModelProperty(position = 2, value = "Timestamp of the Widget Type creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Override public long getCreatedTime() { return super.getCreatedTime(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetType.java b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetType.java index 96c5512dad..6b836c53e8 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetType.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.id.WidgetTypeId; @Data public class WidgetType extends BaseWidgetType { - @ApiModelProperty(position = 7, value = "Complex JSON object that describes the widget type", readOnly = true) + @ApiModelProperty(position = 7, value = "Complex JSON object that describes the widget type", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private transient JsonNode descriptor; public WidgetType() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeDetails.java b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeDetails.java index 2a46091671..1447400721 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeDetails.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeDetails.java @@ -27,11 +27,11 @@ import org.thingsboard.server.common.data.validation.NoXss; public class WidgetTypeDetails extends WidgetType { @Length(fieldName = "image", max = 1000000) - @ApiModelProperty(position = 8, value = "Base64 encoded thumbnail", readOnly = true) + @ApiModelProperty(position = 8, value = "Base64 encoded thumbnail", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String image; @NoXss @Length(fieldName = "description") - @ApiModelProperty(position = 9, value = "Description of the widget", readOnly = true) + @ApiModelProperty(position = 9, value = "Description of the widget", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String description; public WidgetTypeDetails() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeInfo.java index e6d7f591d2..bb7b19fce6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeInfo.java @@ -23,13 +23,13 @@ import org.thingsboard.server.common.data.validation.NoXss; @Data public class WidgetTypeInfo extends BaseWidgetType { - @ApiModelProperty(position = 7, value = "Base64 encoded widget thumbnail", readOnly = true) + @ApiModelProperty(position = 7, value = "Base64 encoded widget thumbnail", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String image; @NoXss - @ApiModelProperty(position = 7, value = "Description of the widget type", readOnly = true) + @ApiModelProperty(position = 7, value = "Description of the widget type", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String description; @NoXss - @ApiModelProperty(position = 8, value = "Type of the widget (timeseries, latest, control, alarm or static)", readOnly = true) + @ApiModelProperty(position = 8, value = "Type of the widget (timeseries, latest, control, alarm or static)", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String widgetType; public WidgetTypeInfo() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundle.java b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundle.java index 4f3de71512..389771e46f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundle.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundle.java @@ -37,34 +37,34 @@ public class WidgetsBundle extends SearchTextBased implements H @Getter @Setter - @ApiModelProperty(position = 3, value = "JSON object with Tenant Id.", readOnly = true) + @ApiModelProperty(position = 3, value = "JSON object with Tenant Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private TenantId tenantId; @NoXss @Length(fieldName = "alias") @Getter @Setter - @ApiModelProperty(position = 4, value = "Unique alias that is used in widget types as a reference widget bundle", readOnly = true) + @ApiModelProperty(position = 4, value = "Unique alias that is used in widget types as a reference widget bundle", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String alias; @NoXss @Length(fieldName = "title") @Getter @Setter - @ApiModelProperty(position = 5, value = "Title used in search and UI", readOnly = true) + @ApiModelProperty(position = 5, value = "Title used in search and UI", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String title; @Length(fieldName = "image", max = 1000000) @Getter @Setter - @ApiModelProperty(position = 6, value = "Base64 encoded thumbnail", readOnly = true) + @ApiModelProperty(position = 6, value = "Base64 encoded thumbnail", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String image; @NoXss @Length(fieldName = "description") @Getter @Setter - @ApiModelProperty(position = 7, value = "Description", readOnly = true) + @ApiModelProperty(position = 7, value = "Description", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String description; @Getter @@ -98,7 +98,7 @@ public class WidgetsBundle extends SearchTextBased implements H return super.getId(); } - @ApiModelProperty(position = 2, value = "Timestamp of the Widget Bundle creation, in milliseconds", example = "1609459200000", readOnly = true) + @ApiModelProperty(position = 2, value = "Timestamp of the Widget Bundle creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Override public long getCreatedTime() { return super.getCreatedTime(); diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml index 0381a19de9..aa4a095c99 100644 --- a/common/edge-api/pom.xml +++ b/common/edge-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.4.0-SNAPSHOT + 3.4.1-SNAPSHOT common org.thingsboard.common diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index 30fdd7053f..4b132d8c76 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -216,8 +216,6 @@ message DeviceProfileUpdateMsg { bytes profileDataBytes = 13; optional string provisionDeviceKey = 14; optional bytes image = 15; - int64 defaultQueueIdMSB = 16; - int64 defaultQueueIdLSB = 17; } message DeviceCredentialsUpdateMsg { diff --git a/common/message/pom.xml b/common/message/pom.xml index 3948f8e7b8..ab4d73f3ee 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.4.0-SNAPSHOT + 3.4.1-SNAPSHOT common org.thingsboard.common 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 bb3be80629..f8eb8c663e 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 @@ -43,7 +43,7 @@ import java.util.UUID; @Slf4j public final class TbMsg implements Serializable { - private final QueueId queueId; + private final String queueName; private final UUID id; private final long ts; private final String type; @@ -67,12 +67,12 @@ public final class TbMsg implements Serializable { return ctx.getAndIncrementRuleNodeCounter(); } - public static TbMsg newMsg(QueueId queueId, String type, EntityId originator, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return newMsg(queueId, type, originator, null, metaData, data, ruleChainId, ruleNodeId); + 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); } - public static TbMsg newMsg(QueueId queueId, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return new TbMsg(queueId, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId, + 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(), type, originator, customerId, metaData.copy(), TbMsgDataType.JSON, data, ruleChainId, ruleNodeId, null, TbMsgCallback.EMPTY); } @@ -87,12 +87,12 @@ public final class TbMsg implements Serializable { // REALLY NEW MSG - public static TbMsg newMsg(QueueId queueId, String type, EntityId originator, TbMsgMetaData metaData, String data) { - return newMsg(queueId, type, originator, null, metaData, data); + public static TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data) { + return newMsg(queueName, type, originator, null, metaData, data); } - public static TbMsg newMsg(QueueId queueId, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { - return new TbMsg(queueId, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId, + public static TbMsg newMsg(String queueName, String 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); } @@ -118,40 +118,40 @@ public final class TbMsg implements Serializable { } public static TbMsg transformMsg(TbMsg tbMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) { - return new TbMsg(tbMsg.queueId, tbMsg.id, tbMsg.ts, type, originator, tbMsg.customerId, metaData.copy(), tbMsg.dataType, + return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, type, originator, tbMsg.customerId, metaData.copy(), tbMsg.dataType, data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.ctx.copy(), tbMsg.callback); } public static TbMsg transformMsg(TbMsg tbMsg, CustomerId customerId) { - return new TbMsg(tbMsg.queueId, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, customerId, tbMsg.metaData, tbMsg.dataType, + return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, customerId, tbMsg.metaData, tbMsg.dataType, tbMsg.data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.ctx.copy(), tbMsg.getCallback()); } public static TbMsg transformMsg(TbMsg tbMsg, RuleChainId ruleChainId) { - return new TbMsg(tbMsg.queueId, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, + return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, tbMsg.data, ruleChainId, null, tbMsg.ctx.copy(), tbMsg.getCallback()); } - public static TbMsg transformMsg(TbMsg tbMsg, QueueId queueId) { - return new TbMsg(queueId, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, + public static TbMsg transformMsg(TbMsg tbMsg, String queueName) { + return new TbMsg(queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, tbMsg.data, tbMsg.getRuleChainId(), null, tbMsg.ctx.copy(), tbMsg.getCallback()); } - public static TbMsg transformMsg(TbMsg tbMsg, RuleChainId ruleChainId, QueueId queueId) { - return new TbMsg(queueId, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, + public static TbMsg transformMsg(TbMsg tbMsg, RuleChainId ruleChainId, String queueName) { + return new TbMsg(queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, tbMsg.data, ruleChainId, null, tbMsg.ctx.copy(), tbMsg.getCallback()); } //used for enqueueForTellNext - public static TbMsg newMsg(TbMsg tbMsg, QueueId queueId, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return new TbMsg(queueId, UUID.randomUUID(), tbMsg.getTs(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.customerId, tbMsg.getMetaData().copy(), + public static TbMsg newMsg(TbMsg tbMsg, String queueName, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { + return new TbMsg(queueName, UUID.randomUUID(), tbMsg.getTs(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.customerId, tbMsg.getMetaData().copy(), tbMsg.getDataType(), tbMsg.getData(), ruleChainId, ruleNodeId, tbMsg.ctx.copy(), TbMsgCallback.EMPTY); } - private TbMsg(QueueId queueId, UUID id, long ts, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data, + private TbMsg(String queueName, UUID id, long ts, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId, TbMsgProcessingCtx ctx, TbMsgCallback callback) { this.id = id; - this.queueId = queueId; + this.queueName = queueName; if (ts > 0) { this.ts = ts; } else { @@ -220,7 +220,7 @@ public final class TbMsg implements Serializable { return builder.build().toByteArray(); } - public static TbMsg fromBytes(QueueId queueId, byte[] data, TbMsgCallback callback) { + public static TbMsg fromBytes(String queueName, byte[] data, TbMsgCallback callback) { try { MsgProtos.TbMsgProto proto = MsgProtos.TbMsgProto.parseFrom(data); TbMsgMetaData metaData = new TbMsgMetaData(proto.getMetaData().getDataMap()); @@ -247,7 +247,7 @@ public final class TbMsg implements Serializable { } TbMsgDataType dataType = TbMsgDataType.values()[proto.getDataType()]; - return new TbMsg(queueId, UUID.fromString(proto.getId()), proto.getTs(), proto.getType(), entityId, customerId, + return new TbMsg(queueName, UUID.fromString(proto.getId()), proto.getTs(), proto.getType(), entityId, customerId, metaData, dataType, proto.getData(), ruleChainId, ruleNodeId, ctx, callback); } catch (InvalidProtocolBufferException e) { throw new IllegalStateException("Could not parse protobuf for TbMsg", e); @@ -259,12 +259,12 @@ public final class TbMsg implements Serializable { } public TbMsg copyWithRuleChainId(RuleChainId ruleChainId, UUID msgId) { - return new TbMsg(this.queueId, msgId, this.ts, this.type, this.originator, this.customerId, + return new TbMsg(this.queueName, msgId, this.ts, this.type, this.originator, this.customerId, this.metaData, this.dataType, this.data, ruleChainId, null, this.ctx, callback); } public TbMsg copyWithRuleNodeId(RuleChainId ruleChainId, RuleNodeId ruleNodeId, UUID msgId) { - return new TbMsg(this.queueId, msgId, this.ts, this.type, this.originator, this.customerId, + return new TbMsg(this.queueName, msgId, this.ts, this.type, this.originator, this.customerId, this.metaData, this.dataType, this.data, ruleChainId, ruleNodeId, this.ctx, callback); } diff --git a/common/pom.xml b/common/pom.xml index 62bb46e81c..33abef1062 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.4.0-SNAPSHOT + 3.4.1-SNAPSHOT thingsboard common diff --git a/common/queue/pom.xml b/common/queue/pom.xml index e2197d566f..e116645618 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.4.0-SNAPSHOT + 3.4.1-SNAPSHOT common org.thingsboard.common 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 index b87049b634..c2974c3e3a 100644 --- 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 @@ -19,6 +19,7 @@ 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 javax.annotation.PostConstruct; import java.util.HashMap; @@ -27,15 +28,15 @@ import java.util.Map; @Component @ConditionalOnExpression("'${queue.type:null}'=='service-bus'") public class TbServiceBusQueueConfigs { - @Value("${queue.service-bus.queue-properties.core}") + @Value("${queue.service-bus.queue-properties.core:}") private String coreProperties; - @Value("${queue.service-bus.queue-properties.rule-engine}") + @Value("${queue.service-bus.queue-properties.rule-engine:}") private String ruleEngineProperties; - @Value("${queue.service-bus.queue-properties.transport-api}") + @Value("${queue.service-bus.queue-properties.transport-api:}") private String transportApiProperties; - @Value("${queue.service-bus.queue-properties.notifications}") + @Value("${queue.service-bus.queue-properties.notifications:}") private String notificationsProperties; - @Value("${queue.service-bus.queue-properties.js-executor}") + @Value("${queue.service-bus.queue-properties.js-executor:}") private String jsExecutorProperties; @Value("${queue.service-bus.queue-properties.version-control:}") private String vcProperties; @@ -64,11 +65,13 @@ public class TbServiceBusQueueConfigs { private Map getConfigs(String properties) { Map configs = new HashMap<>(); - for (String property : properties.split(";")) { - int delimiterPosition = property.indexOf(":"); - String key = property.substring(0, delimiterPosition); - String value = property.substring(delimiterPosition + 1); - configs.put(key, value); + 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); + configs.put(key, value); + } } return configs; } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/AbstractTbQueueTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/AbstractTbQueueTemplate.java index 1b193b7416..5204de42e7 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/AbstractTbQueueTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/AbstractTbQueueTemplate.java @@ -22,7 +22,7 @@ import java.util.UUID; public class AbstractTbQueueTemplate { protected static final String REQUEST_ID_HEADER = "requestId"; protected static final String RESPONSE_TOPIC_HEADER = "responseTopic"; - protected static final String REQUEST_TIME = "requestTime"; + protected static final String EXPIRE_TS_HEADER = "expireTs"; protected byte[] uuidToBytes(UUID uuid) { ByteBuffer buf = ByteBuffer.allocate(16); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java index 9fbe9b1104..1ba1422162 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java @@ -56,6 +56,7 @@ public class DefaultTbQueueRequestTemplate expectedResponse = pendingRequests.remove(requestId); if (expectedResponse == null) { log.debug("[{}] Invalid or stale request, response: {}", requestId, String.valueOf(response).replace("\n", " ")); @@ -216,7 +218,7 @@ public class DefaultTbQueueRequestTemplate future = SettableFuture.create(); ResponseMetaData responseMetaData = new ResponseMetaData<>(currentClockNs + requestTimeoutNs, future, currentClockNs, requestTimeoutNs); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueResponseTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueResponseTemplate.java index 4859b2953b..ed3c3b8cc5 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueResponseTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueResponseTemplate.java @@ -97,8 +97,8 @@ public class DefaultTbQueueResponseTemplate { long currentTime = System.currentTimeMillis(); - long requestTime = bytesToLong(request.getHeaders().get(REQUEST_TIME)); - if (requestTime + requestTimeout >= currentTime) { + long expireTs = bytesToLong(request.getHeaders().get(EXPIRE_TS_HEADER)); + if (expireTs >= currentTime) { byte[] requestIdHeader = request.getHeaders().get(REQUEST_ID_HEADER); if (requestIdHeader == null) { log.error("[{}] Missing requestId in header", request); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java index afbd96625d..d6bfbc9c22 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java @@ -34,6 +34,7 @@ import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent; import org.thingsboard.server.queue.util.AfterStartUp; import javax.annotation.PostConstruct; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -67,8 +68,6 @@ public class HashPartitionService implements PartitionService { private final TenantRoutingInfoService tenantRoutingInfoService; private final QueueRoutingInfoService queueRoutingInfoService; - private final ConcurrentMap queuesById = new ConcurrentHashMap<>(); - private ConcurrentMap> myPartitions = new ConcurrentHashMap<>(); private final ConcurrentMap partitionTopicsMap = new ConcurrentHashMap<>(); @@ -120,7 +119,6 @@ public class HashPartitionService implements PartitionService { QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queue); partitionTopicsMap.put(queueKey, queue.getQueueTopic()); partitionSizesMap.put(queueKey, queue.getPartitions()); - queuesById.put(queue.getQueueId(), queue); }); } @@ -164,8 +162,6 @@ public class HashPartitionService implements PartitionService { public void updateQueue(TransportProtos.QueueUpdateMsg queueUpdateMsg) { TenantId tenantId = new TenantId(new UUID(queueUpdateMsg.getTenantIdMSB(), queueUpdateMsg.getTenantIdLSB())); QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queueUpdateMsg.getQueueName(), tenantId); - QueueRoutingInfo queue = new QueueRoutingInfo(queueUpdateMsg); - queuesById.put(queue.getQueueId(), queue); partitionTopicsMap.put(queueKey, queueUpdateMsg.getQueueTopic()); partitionSizesMap.put(queueKey, queueUpdateMsg.getPartitions()); myPartitions.remove(queueKey); @@ -175,7 +171,6 @@ public class HashPartitionService implements PartitionService { public void removeQueue(TransportProtos.QueueDeleteMsg queueDeleteMsg) { TenantId tenantId = new TenantId(new UUID(queueDeleteMsg.getTenantIdMSB(), queueDeleteMsg.getTenantIdLSB())); QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queueDeleteMsg.getQueueName(), tenantId); - queuesById.remove(new QueueId(new UUID(queueDeleteMsg.getQueueIdMSB(), queueDeleteMsg.getQueueIdLSB()))); myPartitions.remove(queueKey); partitionTopicsMap.remove(queueKey); partitionSizesMap.remove(queueKey); @@ -184,9 +179,7 @@ public class HashPartitionService implements PartitionService { } @Override - @Deprecated - public TopicPartitionInfo resolve(ServiceType serviceType, TenantId tenantId, EntityId entityId, String queueName) { - log.warn("This method is deprecated and will be removed!!!"); + public TopicPartitionInfo resolve(ServiceType serviceType, String queueName, TenantId tenantId, EntityId entityId) { TenantId isolatedOrSystemTenantId = getIsolatedOrSystemTenantId(serviceType, tenantId); QueueKey queueKey = new QueueKey(serviceType, queueName, isolatedOrSystemTenantId); if (!partitionSizesMap.containsKey(queueKey)) { @@ -200,32 +193,6 @@ public class HashPartitionService implements PartitionService { return resolve(serviceType, null, tenantId, entityId); } - @Override - public TopicPartitionInfo resolve(ServiceType serviceType, QueueId queueId, TenantId tenantId, EntityId entityId) { - QueueKey queueKey; - if (queueId == null) { - queueKey = getMainQueueKey(serviceType, tenantId); - } else { - QueueRoutingInfo queueRoutingInfo = queuesById.get(queueId); - - if (queueRoutingInfo == null) { - log.debug("Queue was removed but still used in CheckPoint rule node. [{}][{}]", tenantId, entityId); - queueKey = getMainQueueKey(serviceType, tenantId); - } else if (!queueRoutingInfo.getTenantId().equals(getIsolatedOrSystemTenantId(serviceType, tenantId))) { - log.debug("Tenant profile was changed but CheckPoint rule node still uses the queue from system level. [{}][{}]", tenantId, entityId); - queueKey = getMainQueueKey(serviceType, tenantId); - } else { - queueKey = new QueueKey(serviceType, queueRoutingInfo); - } - } - - return resolve(queueKey, entityId); - } - - private QueueKey getMainQueueKey(ServiceType serviceType, TenantId tenantId) { - return new QueueKey(serviceType, getIsolatedOrSystemTenantId(serviceType, tenantId)); - } - private TopicPartitionInfo resolve(QueueKey queueKey, EntityId entityId) { int hash = hashFunction.newHasher() .putLong(entityId.getId().getMostSignificantBits()) @@ -254,7 +221,7 @@ public class HashPartitionService implements PartitionService { myPartitions = new ConcurrentHashMap<>(); partitionSizesMap.forEach((queueKey, size) -> { for (int i = 0; i < size; i++) { - ServiceInfo serviceInfo = resolveByPartitionIdx(queueServicesMap.get(queueKey), i); + ServiceInfo serviceInfo = resolveByPartitionIdx(queueServicesMap.get(queueKey), queueKey, i); if (currentService.equals(serviceInfo)) { myPartitions.computeIfAbsent(queueKey, key -> new ArrayList<>()).add(i); } @@ -398,8 +365,6 @@ public class HashPartitionService implements PartitionService { throw new RuntimeException("Tenant not found!"); } switch (serviceType) { - case TB_CORE: - return routingInfo.isIsolatedTbCore(); case TB_RULE_ENGINE: return routingInfo.isIsolatedTbRuleEngine(); default: @@ -434,11 +399,21 @@ public class HashPartitionService implements PartitionService { } } - private ServiceInfo resolveByPartitionIdx(List servers, Integer partitionIdx) { + protected ServiceInfo resolveByPartitionIdx(List servers, QueueKey queueKey, int partition) { if (servers == null || servers.isEmpty()) { return null; } - return servers.get(partitionIdx % servers.size()); + + if (!ServiceType.TB_RULE_ENGINE.equals(queueKey.getType()) || TenantId.SYS_TENANT_ID.equals(queueKey.getTenantId())) { + return servers.get(partition % servers.size()); + } else { + int hash = hashFunction.newHasher().putLong(queueKey.getTenantId().getId().getMostSignificantBits()) + .putLong(queueKey.getTenantId().getId().getLeastSignificantBits()) + .putString(queueKey.getQueueName(), StandardCharsets.UTF_8) + .hash().asInt(); + + return servers.get(Math.abs((hash + partition) % servers.size())); + } } public static HashFunction forName(String name) { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java index 3844bf02c9..f65a83b287 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java @@ -32,13 +32,10 @@ import java.util.UUID; */ public interface PartitionService { - @Deprecated - TopicPartitionInfo resolve(ServiceType serviceType, TenantId tenantId, EntityId entityId, String queueName); + TopicPartitionInfo resolve(ServiceType serviceType, String queueName, TenantId tenantId, EntityId entityId); TopicPartitionInfo resolve(ServiceType serviceType, TenantId tenantId, EntityId entityId); - TopicPartitionInfo resolve(ServiceType serviceType, QueueId queueId, TenantId tenantId, EntityId entityId); - /** * Received from the Discovery service when network topology is changed. * @param currentService - current service information {@link org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TenantRoutingInfo.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TenantRoutingInfo.java index 01c1288635..165bf9459a 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TenantRoutingInfo.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TenantRoutingInfo.java @@ -21,6 +21,5 @@ import org.thingsboard.server.common.data.id.TenantId; @Data public class TenantRoutingInfo { private final TenantId tenantId; - private final boolean isolatedTbCore; private final boolean isolatedTbRuleEngine; } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaAdmin.java b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaAdmin.java index d5a9a34dc7..530a7a1a58 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaAdmin.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaAdmin.java @@ -51,7 +51,7 @@ public class TbKafkaAdmin implements TbQueueAdmin { log.error("Failed to get all topics.", e); } - String numPartitionsStr = topicConfigs.get("partitions"); + String numPartitionsStr = topicConfigs.get(TbKafkaTopicConfigs.NUM_PARTITIONS_SETTING); if (numPartitionsStr != null) { numPartitions = Integer.parseInt(numPartitionsStr); topicConfigs.remove("partitions"); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaTopicConfigs.java b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaTopicConfigs.java index 75d0bd9b2d..c02a1af9d5 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaTopicConfigs.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaTopicConfigs.java @@ -28,32 +28,37 @@ import java.util.Map; @Component @ConditionalOnProperty(prefix = "queue", value = "type", havingValue = "kafka") public class TbKafkaTopicConfigs { - @Value("${queue.kafka.topic-properties.core}") + public static final String NUM_PARTITIONS_SETTING = "partitions"; + + @Value("${queue.kafka.topic-properties.core:}") private String coreProperties; - @Value("${queue.kafka.topic-properties.rule-engine}") + @Value("${queue.kafka.topic-properties.rule-engine:}") private String ruleEngineProperties; - @Value("${queue.kafka.topic-properties.transport-api}") + @Value("${queue.kafka.topic-properties.transport-api:}") private String transportApiProperties; - @Value("${queue.kafka.topic-properties.notifications}") + @Value("${queue.kafka.topic-properties.notifications:}") private String notificationsProperties; - @Value("${queue.kafka.topic-properties.js-executor}") + @Value("${queue.kafka.topic-properties.js-executor:}") private String jsExecutorProperties; @Value("${queue.kafka.topic-properties.ota-updates:}") private String fwUpdatesProperties; @Value("${queue.kafka.topic-properties.version-control:}") private String vcProperties; - @Getter private Map coreConfigs; @Getter private Map ruleEngineConfigs; @Getter - private Map transportApiConfigs; + private Map transportApiRequestConfigs; + @Getter + private Map transportApiResponseConfigs; @Getter private Map notificationsConfigs; @Getter - private Map jsExecutorConfigs; + private Map jsExecutorRequestConfigs; + @Getter + private Map jsExecutorResponseConfigs; @Getter private Map fwUpdatesConfigs; @Getter @@ -63,9 +68,13 @@ public class TbKafkaTopicConfigs { private void init() { coreConfigs = getConfigs(coreProperties); ruleEngineConfigs = getConfigs(ruleEngineProperties); - transportApiConfigs = getConfigs(transportApiProperties); + transportApiRequestConfigs = getConfigs(transportApiProperties); + transportApiResponseConfigs = getConfigs(transportApiProperties); + transportApiResponseConfigs.put(NUM_PARTITIONS_SETTING, "1"); notificationsConfigs = getConfigs(notificationsProperties); - jsExecutorConfigs = getConfigs(jsExecutorProperties); + jsExecutorRequestConfigs = getConfigs(jsExecutorProperties); + jsExecutorResponseConfigs = getConfigs(jsExecutorProperties); + jsExecutorResponseConfigs.put(NUM_PARTITIONS_SETTING, "1"); fwUpdatesConfigs = getConfigs(fwUpdatesProperties); vcConfigs = getConfigs(vcProperties); } 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 index 2ca5090aed..41efe08c1c 100644 --- 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 @@ -76,6 +76,7 @@ public class AwsSqsMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng private final TbQueueAdmin jsExecutorAdmin; private final TbQueueAdmin transportApiAdmin; private final TbQueueAdmin notificationAdmin; + private final TbQueueAdmin otaAdmin; private final TbQueueAdmin vcAdmin; public AwsSqsMonolithQueueFactory(NotificationsTopicService notificationsTopicService, TbQueueCoreSettings coreSettings, @@ -102,6 +103,7 @@ public class AwsSqsMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng 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()); } @@ -210,13 +212,13 @@ public class AwsSqsMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng @Override public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(coreAdmin, sqsSettings, coreSettings.getOtaPackageTopic(), + return new TbAwsSqsConsumerTemplate<>(otaAdmin, sqsSettings, coreSettings.getOtaPackageTopic(), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getOtaPackageTopic()); + return new TbAwsSqsProducerTemplate<>(otaAdmin, sqsSettings, coreSettings.getOtaPackageTopic()); } @Override @@ -241,6 +243,9 @@ public class AwsSqsMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng if (notificationAdmin != null) { notificationAdmin.destroy(); } + if (otaAdmin != null) { + otaAdmin.destroy(); + } if (vcAdmin != null) { vcAdmin.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 index 89b45e2822..17dcd07919 100644 --- 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 @@ -45,6 +45,7 @@ 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; @@ -66,18 +67,22 @@ public class AwsSqsTbCoreQueueFactory implements TbCoreQueueFactory { private final TbServiceInfoProvider serviceInfoProvider; private final TbQueueRemoteJsInvokeSettings jsInvokeSettings; private final TbQueueTransportNotificationSettings transportNotificationSettings; + private final TbQueueVersionControlSettings vcSettings; 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; public AwsSqsTbCoreQueueFactory(TbAwsSqsSettings sqsSettings, TbQueueCoreSettings coreSettings, TbQueueTransportApiSettings transportApiSettings, TbQueueRuleEngineSettings ruleEngineSettings, NotificationsTopicService notificationsTopicService, + TbQueueVersionControlSettings vcSettings, TbServiceInfoProvider serviceInfoProvider, TbQueueRemoteJsInvokeSettings jsInvokeSettings, TbAwsSqsQueueAttributes sqsQueueAttributes, @@ -90,12 +95,15 @@ public class AwsSqsTbCoreQueueFactory implements TbCoreQueueFactory { 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()); } @Override @@ -183,19 +191,18 @@ public class AwsSqsTbCoreQueueFactory implements TbCoreQueueFactory { @Override public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(coreAdmin, sqsSettings, coreSettings.getOtaPackageTopic(), + return new TbAwsSqsConsumerTemplate<>(otaAdmin, sqsSettings, coreSettings.getOtaPackageTopic(), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getOtaPackageTopic()); + return new TbAwsSqsProducerTemplate<>(otaAdmin, sqsSettings, coreSettings.getOtaPackageTopic()); } @Override public TbQueueProducer> createVersionControlMsgProducer() { - //TODO: version-control - return null; + return new TbAwsSqsProducerTemplate<>(vcAdmin, sqsSettings, vcSettings.getTopic()); } @PreDestroy @@ -215,5 +222,11 @@ public class AwsSqsTbCoreQueueFactory implements TbCoreQueueFactory { if (notificationAdmin != null) { notificationAdmin.destroy(); } + if (otaAdmin != null) { + otaAdmin.destroy(); + } + if (vcAdmin != null) { + vcAdmin.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 index 9d9fd29078..94df62eb3a 100644 --- 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 @@ -39,6 +39,7 @@ import org.thingsboard.server.queue.settings.TbQueueCoreSettings; 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.settings.TbQueueVersionControlSettings; import org.thingsboard.server.queue.sqs.TbAwsSqsAdmin; import org.thingsboard.server.queue.sqs.TbAwsSqsConsumerTemplate; import org.thingsboard.server.queue.sqs.TbAwsSqsProducerTemplate; @@ -64,6 +65,7 @@ public class AwsSqsTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory private final TbQueueAdmin ruleEngineAdmin; private final TbQueueAdmin jsExecutorAdmin; private final TbQueueAdmin notificationAdmin; + private final TbQueueAdmin otaAdmin; public AwsSqsTbRuleEngineQueueFactory(NotificationsTopicService notificationsTopicService, TbQueueCoreSettings coreSettings, TbQueueRuleEngineSettings ruleEngineSettings, @@ -84,6 +86,7 @@ public class AwsSqsTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory 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()); } @Override @@ -154,7 +157,7 @@ public class AwsSqsTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory @Override public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getOtaPackageTopic()); + return new TbAwsSqsProducerTemplate<>(otaAdmin, sqsSettings, coreSettings.getOtaPackageTopic()); } @@ -172,6 +175,9 @@ public class AwsSqsTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory if (notificationAdmin != null) { notificationAdmin.destroy(); } + if (otaAdmin != null) { + otaAdmin.destroy(); + } } } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java index b24ae700bb..54a1a8b3a4 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java @@ -75,8 +75,10 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi private final TbQueueAdmin coreAdmin; private final TbQueueAdmin ruleEngineAdmin; - private final TbQueueAdmin jsExecutorAdmin; - private final TbQueueAdmin transportApiAdmin; + private final TbQueueAdmin jsExecutorRequestAdmin; + private final TbQueueAdmin jsExecutorResponseAdmin; + private final TbQueueAdmin transportApiRequestAdmin; + private final TbQueueAdmin transportApiResponseAdmin; private final TbQueueAdmin notificationAdmin; private final TbQueueAdmin fwUpdatesAdmin; private final TbQueueAdmin vcAdmin; @@ -106,8 +108,10 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi this.coreAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getCoreConfigs()); this.ruleEngineAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getRuleEngineConfigs()); - this.jsExecutorAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getJsExecutorConfigs()); - this.transportApiAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getTransportApiConfigs()); + this.jsExecutorRequestAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getJsExecutorRequestConfigs()); + this.jsExecutorResponseAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getJsExecutorResponseConfigs()); + this.transportApiRequestAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getTransportApiRequestConfigs()); + this.transportApiResponseAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getTransportApiResponseConfigs()); this.notificationAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getNotificationsConfigs()); this.fwUpdatesAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getFwUpdatesConfigs()); this.vcAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getVcConfigs()); @@ -237,7 +241,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi consumerBuilder.clientId("monolith-transport-api-consumer-" + serviceInfoProvider.getServiceId()); consumerBuilder.groupId("monolith-transport-api-consumer"); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); - consumerBuilder.admin(transportApiAdmin); + consumerBuilder.admin(transportApiRequestAdmin); consumerBuilder.statsService(consumerStatsService); return consumerBuilder.build(); } @@ -248,7 +252,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi requestBuilder.settings(kafkaSettings); requestBuilder.clientId("monolith-transport-api-producer-" + serviceInfoProvider.getServiceId()); requestBuilder.defaultTopic(transportApiSettings.getResponsesTopic()); - requestBuilder.admin(transportApiAdmin); + requestBuilder.admin(transportApiResponseAdmin); return requestBuilder.build(); } @@ -259,7 +263,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi requestBuilder.settings(kafkaSettings); requestBuilder.clientId("producer-js-invoke-" + serviceInfoProvider.getServiceId()); requestBuilder.defaultTopic(jsInvokeSettings.getRequestTopic()); - requestBuilder.admin(jsExecutorAdmin); + requestBuilder.admin(jsExecutorRequestAdmin); TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> responseBuilder = TbKafkaConsumerTemplate.builder(); responseBuilder.settings(kafkaSettings); @@ -273,11 +277,11 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi } ); responseBuilder.statsService(consumerStatsService); - responseBuilder.admin(jsExecutorAdmin); + responseBuilder.admin(jsExecutorResponseAdmin); DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder , TbProtoQueueMsg> builder = DefaultTbQueueRequestTemplate.builder(); - builder.queueAdmin(jsExecutorAdmin); + builder.queueAdmin(jsExecutorResponseAdmin); builder.requestTemplate(requestBuilder.build()); builder.responseTemplate(responseBuilder.build()); builder.maxPendingRequests(jsInvokeSettings.getMaxPendingRequests()); @@ -350,11 +354,17 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi if (ruleEngineAdmin != null) { ruleEngineAdmin.destroy(); } - if (jsExecutorAdmin != null) { - jsExecutorAdmin.destroy(); + if (jsExecutorRequestAdmin != null) { + jsExecutorRequestAdmin.destroy(); } - if (transportApiAdmin != null) { - transportApiAdmin.destroy(); + if (jsExecutorResponseAdmin != null) { + jsExecutorResponseAdmin.destroy(); + } + if (transportApiRequestAdmin != null) { + transportApiRequestAdmin.destroy(); + } + if (transportApiResponseAdmin != null) { + transportApiResponseAdmin.destroy(); } if (notificationAdmin != null) { notificationAdmin.destroy(); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java index f476a068d2..6a7c6380fa 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java @@ -73,8 +73,10 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { private final TbQueueAdmin coreAdmin; private final TbQueueAdmin ruleEngineAdmin; - private final TbQueueAdmin jsExecutorAdmin; - private final TbQueueAdmin transportApiAdmin; + private final TbQueueAdmin jsExecutorRequestAdmin; + private final TbQueueAdmin jsExecutorResponseAdmin; + private final TbQueueAdmin transportApiRequestAdmin; + private final TbQueueAdmin transportApiResponseAdmin; private final TbQueueAdmin notificationAdmin; private final TbQueueAdmin fwUpdatesAdmin; private final TbQueueAdmin vcAdmin; @@ -103,8 +105,10 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { this.coreAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getCoreConfigs()); this.ruleEngineAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getRuleEngineConfigs()); - this.jsExecutorAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getJsExecutorConfigs()); - this.transportApiAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getTransportApiConfigs()); + this.jsExecutorRequestAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getJsExecutorRequestConfigs()); + this.jsExecutorResponseAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getJsExecutorResponseConfigs()); + this.transportApiRequestAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getTransportApiRequestConfigs()); + this.transportApiResponseAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getTransportApiResponseConfigs()); this.notificationAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getNotificationsConfigs()); this.fwUpdatesAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getFwUpdatesConfigs()); this.vcAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getVcConfigs()); @@ -194,7 +198,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { consumerBuilder.clientId("tb-core-transport-api-consumer-" + serviceInfoProvider.getServiceId()); consumerBuilder.groupId("tb-core-transport-api-consumer"); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); - consumerBuilder.admin(transportApiAdmin); + consumerBuilder.admin(transportApiRequestAdmin); consumerBuilder.statsService(consumerStatsService); return consumerBuilder.build(); } @@ -205,7 +209,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { requestBuilder.settings(kafkaSettings); requestBuilder.clientId("tb-core-transport-api-producer-" + serviceInfoProvider.getServiceId()); requestBuilder.defaultTopic(transportApiSettings.getResponsesTopic()); - requestBuilder.admin(transportApiAdmin); + requestBuilder.admin(transportApiResponseAdmin); return requestBuilder.build(); } @@ -216,7 +220,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { requestBuilder.settings(kafkaSettings); requestBuilder.clientId("producer-js-invoke-" + serviceInfoProvider.getServiceId()); requestBuilder.defaultTopic(jsInvokeSettings.getRequestTopic()); - requestBuilder.admin(jsExecutorAdmin); + requestBuilder.admin(jsExecutorRequestAdmin); TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> responseBuilder = TbKafkaConsumerTemplate.builder(); responseBuilder.settings(kafkaSettings); @@ -229,12 +233,12 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { return new TbProtoQueueMsg<>(msg.getKey(), builder.build(), msg.getHeaders()); } ); - responseBuilder.admin(jsExecutorAdmin); + responseBuilder.admin(jsExecutorResponseAdmin); responseBuilder.statsService(consumerStatsService); DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder , TbProtoQueueMsg> builder = DefaultTbQueueRequestTemplate.builder(); - builder.queueAdmin(jsExecutorAdmin); + builder.queueAdmin(jsExecutorResponseAdmin); builder.requestTemplate(requestBuilder.build()); builder.responseTemplate(responseBuilder.build()); builder.maxPendingRequests(jsInvokeSettings.getMaxPendingRequests()); @@ -307,11 +311,17 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { if (ruleEngineAdmin != null) { ruleEngineAdmin.destroy(); } - if (jsExecutorAdmin != null) { - jsExecutorAdmin.destroy(); + if (jsExecutorRequestAdmin != null) { + jsExecutorRequestAdmin.destroy(); } - if (transportApiAdmin != null) { - transportApiAdmin.destroy(); + if (jsExecutorResponseAdmin != null) { + jsExecutorResponseAdmin.destroy(); + } + if (transportApiRequestAdmin != null) { + transportApiRequestAdmin.destroy(); + } + if (transportApiResponseAdmin != null) { + transportApiResponseAdmin.destroy(); } if (notificationAdmin != null) { notificationAdmin.destroy(); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java index 12a51fc8fd..13f3361232 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java @@ -68,7 +68,8 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { private final TbQueueAdmin coreAdmin; private final TbQueueAdmin ruleEngineAdmin; - private final TbQueueAdmin jsExecutorAdmin; + private final TbQueueAdmin jsExecutorRequestAdmin; + private final TbQueueAdmin jsExecutorResponseAdmin; private final TbQueueAdmin notificationAdmin; private final TbQueueAdmin fwUpdatesAdmin; private final AtomicLong consumerCount = new AtomicLong(); @@ -92,7 +93,8 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { this.coreAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getCoreConfigs()); this.ruleEngineAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getRuleEngineConfigs()); - this.jsExecutorAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getJsExecutorConfigs()); + this.jsExecutorRequestAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getJsExecutorRequestConfigs()); + this.jsExecutorResponseAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getJsExecutorResponseConfigs()); this.notificationAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getNotificationsConfigs()); this.fwUpdatesAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getFwUpdatesConfigs()); } @@ -191,7 +193,7 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { requestBuilder.settings(kafkaSettings); requestBuilder.clientId("producer-js-invoke-" + serviceInfoProvider.getServiceId()); requestBuilder.defaultTopic(jsInvokeSettings.getRequestTopic()); - requestBuilder.admin(jsExecutorAdmin); + requestBuilder.admin(jsExecutorRequestAdmin); TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> responseBuilder = TbKafkaConsumerTemplate.builder(); responseBuilder.settings(kafkaSettings); @@ -204,12 +206,12 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { return new TbProtoQueueMsg<>(msg.getKey(), builder.build(), msg.getHeaders()); } ); - responseBuilder.admin(jsExecutorAdmin); + responseBuilder.admin(jsExecutorResponseAdmin); responseBuilder.statsService(consumerStatsService); DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder , TbProtoQueueMsg> builder = DefaultTbQueueRequestTemplate.builder(); - builder.queueAdmin(jsExecutorAdmin); + builder.queueAdmin(jsExecutorResponseAdmin); builder.requestTemplate(requestBuilder.build()); builder.responseTemplate(responseBuilder.build()); builder.maxPendingRequests(jsInvokeSettings.getMaxPendingRequests()); @@ -236,8 +238,11 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { if (ruleEngineAdmin != null) { ruleEngineAdmin.destroy(); } - if (jsExecutorAdmin != null) { - jsExecutorAdmin.destroy(); + if (jsExecutorRequestAdmin != null) { + jsExecutorRequestAdmin.destroy(); + } + if (jsExecutorResponseAdmin != null) { + jsExecutorResponseAdmin.destroy(); } if (notificationAdmin != null) { notificationAdmin.destroy(); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbTransportQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbTransportQueueFactory.java index 8e692573c7..c05120fbf2 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbTransportQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbTransportQueueFactory.java @@ -59,7 +59,8 @@ public class KafkaTbTransportQueueFactory implements TbTransportQueueFactory { private final TbQueueAdmin coreAdmin; private final TbQueueAdmin ruleEngineAdmin; - private final TbQueueAdmin transportApiAdmin; + private final TbQueueAdmin transportApiRequestAdmin; + private final TbQueueAdmin transportApiResponseAdmin; private final TbQueueAdmin notificationAdmin; public KafkaTbTransportQueueFactory(TbKafkaSettings kafkaSettings, @@ -80,7 +81,8 @@ public class KafkaTbTransportQueueFactory implements TbTransportQueueFactory { this.coreAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getCoreConfigs()); this.ruleEngineAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getRuleEngineConfigs()); - this.transportApiAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getTransportApiConfigs()); + this.transportApiRequestAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getTransportApiRequestConfigs()); + this.transportApiResponseAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getTransportApiResponseConfigs()); this.notificationAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getNotificationsConfigs()); } @@ -90,7 +92,7 @@ public class KafkaTbTransportQueueFactory implements TbTransportQueueFactory { requestBuilder.settings(kafkaSettings); requestBuilder.clientId("transport-api-request-" + serviceInfoProvider.getServiceId()); requestBuilder.defaultTopic(transportApiSettings.getRequestsTopic()); - requestBuilder.admin(transportApiAdmin); + requestBuilder.admin(transportApiRequestAdmin); TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> responseBuilder = TbKafkaConsumerTemplate.builder(); responseBuilder.settings(kafkaSettings); @@ -98,12 +100,12 @@ public class KafkaTbTransportQueueFactory implements TbTransportQueueFactory { responseBuilder.clientId("transport-api-response-" + serviceInfoProvider.getServiceId()); responseBuilder.groupId("transport-node-" + serviceInfoProvider.getServiceId()); responseBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiResponseMsg.parseFrom(msg.getData()), msg.getHeaders())); - responseBuilder.admin(transportApiAdmin); + responseBuilder.admin(transportApiResponseAdmin); responseBuilder.statsService(consumerStatsService); DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder , TbProtoQueueMsg> templateBuilder = DefaultTbQueueRequestTemplate.builder(); - templateBuilder.queueAdmin(transportApiAdmin); + templateBuilder.queueAdmin(transportApiResponseAdmin); templateBuilder.requestTemplate(requestBuilder.build()); templateBuilder.responseTemplate(responseBuilder.build()); templateBuilder.maxPendingRequests(transportApiSettings.getMaxPendingRequests()); @@ -163,8 +165,11 @@ public class KafkaTbTransportQueueFactory implements TbTransportQueueFactory { if (ruleEngineAdmin != null) { ruleEngineAdmin.destroy(); } - if (transportApiAdmin != null) { - transportApiAdmin.destroy(); + if (transportApiRequestAdmin != null) { + transportApiRequestAdmin.destroy(); + } + if (transportApiResponseAdmin != null) { + transportApiResponseAdmin.destroy(); } if (notificationAdmin != null) { notificationAdmin.destroy(); 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 index a4819fdad4..1f5430a0bb 100644 --- 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 @@ -19,6 +19,7 @@ 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 javax.annotation.PostConstruct; import java.util.HashMap; @@ -27,15 +28,15 @@ import java.util.Map; @Component @ConditionalOnExpression("'${queue.type:null}'=='pubsub'") public class TbPubSubSubscriptionSettings { - @Value("${queue.pubsub.queue-properties.core}") + @Value("${queue.pubsub.queue-properties.core:}") private String coreProperties; - @Value("${queue.pubsub.queue-properties.rule-engine}") + @Value("${queue.pubsub.queue-properties.rule-engine:}") private String ruleEngineProperties; - @Value("${queue.pubsub.queue-properties.transport-api}") + @Value("${queue.pubsub.queue-properties.transport-api:}") private String transportApiProperties; - @Value("${queue.pubsub.queue-properties.notifications}") + @Value("${queue.pubsub.queue-properties.notifications:}") private String notificationsProperties; - @Value("${queue.pubsub.queue-properties.js-executor}") + @Value("${queue.pubsub.queue-properties.js-executor:}") private String jsExecutorProperties; @Value("${queue.pubsub.queue-properties.version-control:}") private String vcProperties; @@ -65,11 +66,13 @@ public class TbPubSubSubscriptionSettings { private Map getSettings(String properties) { Map configs = new HashMap<>(); - for (String property : properties.split(";")) { - int delimiterPosition = property.indexOf(":"); - String key = property.substring(0, delimiterPosition); - String value = property.substring(delimiterPosition + 1); - configs.put(key, value); + 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); + configs.put(key, value); + } } return configs; } 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 index 25c78a96c5..af3602fd73 100644 --- 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 @@ -19,6 +19,7 @@ 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 javax.annotation.PostConstruct; import java.util.HashMap; @@ -28,15 +29,15 @@ import java.util.regex.Pattern; @Component @ConditionalOnExpression("'${queue.type:null}'=='rabbitmq'") public class TbRabbitMqQueueArguments { - @Value("${queue.rabbitmq.queue-properties.core}") + @Value("${queue.rabbitmq.queue-properties.core:}") private String coreProperties; - @Value("${queue.rabbitmq.queue-properties.rule-engine}") + @Value("${queue.rabbitmq.queue-properties.rule-engine:}") private String ruleEngineProperties; - @Value("${queue.rabbitmq.queue-properties.transport-api}") + @Value("${queue.rabbitmq.queue-properties.transport-api:}") private String transportApiProperties; - @Value("${queue.rabbitmq.queue-properties.notifications}") + @Value("${queue.rabbitmq.queue-properties.notifications:}") private String notificationsProperties; - @Value("${queue.rabbitmq.queue-properties.js-executor}") + @Value("${queue.rabbitmq.queue-properties.js-executor:}") private String jsExecutorProperties; @Value("${queue.rabbitmq.queue-properties.version-control:}") private String vcProperties; @@ -66,11 +67,13 @@ public class TbRabbitMqQueueArguments { private Map getArgs(String properties) { Map configs = new HashMap<>(); - 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)); + 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; } 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 index 58704f62a7..2e28b56076 100644 --- 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 @@ -20,6 +20,7 @@ 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 javax.annotation.PostConstruct; import java.util.HashMap; @@ -28,16 +29,18 @@ import java.util.Map; @Component @ConditionalOnExpression("'${queue.type:null}'=='aws-sqs'") public class TbAwsSqsQueueAttributes { - @Value("${queue.aws-sqs.queue-properties.core}") + @Value("${queue.aws-sqs.queue-properties.core:}") private String coreProperties; - @Value("${queue.aws-sqs.queue-properties.rule-engine}") + @Value("${queue.aws-sqs.queue-properties.rule-engine:}") private String ruleEngineProperties; - @Value("${queue.aws-sqs.queue-properties.transport-api}") + @Value("${queue.aws-sqs.queue-properties.transport-api:}") private String transportApiProperties; - @Value("${queue.aws-sqs.queue-properties.notifications}") + @Value("${queue.aws-sqs.queue-properties.notifications:}") private String notificationsProperties; - @Value("${queue.aws-sqs.queue-properties.js-executor}") + @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; @@ -52,6 +55,8 @@ public class TbAwsSqsQueueAttributes { @Getter private Map jsExecutorAttributes; @Getter + private Map otaAttributes; + @Getter private Map vcAttributes; private final Map defaultAttributes = new HashMap<>(); @@ -65,19 +70,21 @@ public class TbAwsSqsQueueAttributes { transportApiAttributes = getConfigs(transportApiProperties); notificationsAttributes = getConfigs(notificationsProperties); jsExecutorAttributes = getConfigs(jsExecutorProperties); + otaAttributes = getConfigs(otaProperties); vcAttributes = getConfigs(vcProperties); } private Map getConfigs(String properties) { - Map configs = new HashMap<>(); - 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); + Map configs = new HashMap<>(defaultAttributes); + 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); + } } - configs.putAll(defaultAttributes); return configs; } diff --git a/common/stats/pom.xml b/common/stats/pom.xml index 7802793d64..7648eb911a 100644 --- a/common/stats/pom.xml +++ b/common/stats/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.4.0-SNAPSHOT + 3.4.1-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml index 98cf0b807a..6248f11a6c 100644 --- a/common/transport/coap/pom.xml +++ b/common/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.4.0-SNAPSHOT + 3.4.1-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml index 1096d58f2a..a55dea2f2b 100644 --- a/common/transport/http/pom.xml +++ b/common/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.4.0-SNAPSHOT + 3.4.1-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/lwm2m/pom.xml b/common/transport/lwm2m/pom.xml index 2fb1f533aa..29e74a7931 100644 --- a/common/transport/lwm2m/pom.xml +++ b/common/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.4.0-SNAPSHOT + 3.4.1-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml index c029e7c9ac..2542e5914e 100644 --- a/common/transport/mqtt/pom.xml +++ b/common/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.4.0-SNAPSHOT + 3.4.1-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/pom.xml b/common/transport/pom.xml index a061c963f4..be0da470c3 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.4.0-SNAPSHOT + 3.4.1-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml index ee1d8f2a0d..7bcf4dc137 100644 --- a/common/transport/snmp/pom.xml +++ b/common/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.common - 3.4.0-SNAPSHOT + 3.4.1-SNAPSHOT transport diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index 51373e2a4a..308768231e 100644 --- a/common/transport/transport-api/pom.xml +++ b/common/transport/transport-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.4.0-SNAPSHOT + 3.4.1-SNAPSHOT transport org.thingsboard.common.transport 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 68f8db272b..a63e4a01a2 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 @@ -1088,7 +1088,7 @@ public class DefaultTransportService implements TransportService { } private void sendToRuleEngine(TenantId tenantId, TbMsg tbMsg, TbQueueCallback callback) { - TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tbMsg.getQueueId(), tenantId, tbMsg.getOriginator()); + TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tbMsg.getQueueName(), tenantId, tbMsg.getOriginator()); if (log.isTraceEnabled()) { log.trace("[{}][{}] Pushing to topic {} message {}", tenantId, tbMsg.getOriginator(), tpi.getFullTopicName(), tbMsg); } @@ -1105,18 +1105,18 @@ public class DefaultTransportService implements TransportService { DeviceProfileId deviceProfileId = new DeviceProfileId(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); DeviceProfile deviceProfile = deviceProfileCache.get(deviceProfileId); RuleChainId ruleChainId; - QueueId queueId; + String queueName; if (deviceProfile == null) { log.warn("[{}] Device profile is null!", deviceProfileId); ruleChainId = null; - queueId = null; + queueName = null; } else { ruleChainId = deviceProfile.getDefaultRuleChainId(); - queueId = deviceProfile.getDefaultQueueId(); + queueName = deviceProfile.getDefaultQueueName(); } - TbMsg tbMsg = TbMsg.newMsg(queueId, sessionMsgType.name(), deviceId, customerId, metaData, gson.toJson(json), ruleChainId, null); + TbMsg tbMsg = TbMsg.newMsg(queueName, sessionMsgType.name(), deviceId, customerId, metaData, gson.toJson(json), ruleChainId, null); sendToRuleEngine(tenantId, tbMsg, callback); } diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportTenantRoutingInfoService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportTenantRoutingInfoService.java index fa66a355eb..6afc07f556 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportTenantRoutingInfoService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportTenantRoutingInfoService.java @@ -38,7 +38,7 @@ public class TransportTenantRoutingInfoService implements TenantRoutingInfoServi @Override public TenantRoutingInfo getRoutingInfo(TenantId tenantId) { TenantProfile profile = tenantProfileCache.get(tenantId); - return new TenantRoutingInfo(tenantId, profile.isIsolatedTbCore(), profile.isIsolatedTbRuleEngine()); + return new TenantRoutingInfo(tenantId, profile.isIsolatedTbRuleEngine()); } } diff --git a/common/util/pom.xml b/common/util/pom.xml index 147497ef04..cd45ff96d7 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.4.0-SNAPSHOT + 3.4.1-SNAPSHOT common org.thingsboard.common diff --git a/common/util/src/main/java/org/thingsboard/common/util/AbstractListeningExecutor.java b/common/util/src/main/java/org/thingsboard/common/util/AbstractListeningExecutor.java index edace3cee5..f7da0b07a7 100644 --- a/common/util/src/main/java/org/thingsboard/common/util/AbstractListeningExecutor.java +++ b/common/util/src/main/java/org/thingsboard/common/util/AbstractListeningExecutor.java @@ -49,6 +49,10 @@ public abstract class AbstractListeningExecutor implements ListeningExecutor { return service.submit(task); } + public ListenableFuture executeAsync(Runnable task) { + return service.submit(task); + } + @Override public void execute(Runnable command) { service.execute(command); diff --git a/common/util/src/main/java/org/thingsboard/common/util/ListeningExecutor.java b/common/util/src/main/java/org/thingsboard/common/util/ListeningExecutor.java index 2726868f19..e63613d935 100644 --- a/common/util/src/main/java/org/thingsboard/common/util/ListeningExecutor.java +++ b/common/util/src/main/java/org/thingsboard/common/util/ListeningExecutor.java @@ -24,8 +24,19 @@ public interface ListeningExecutor extends Executor { ListenableFuture executeAsync(Callable task); + default ListenableFuture executeAsync(Runnable task) { + return executeAsync(() -> { + task.run(); + return null; + }); + } + default ListenableFuture submit(Callable task) { return executeAsync(task); } + default ListenableFuture submit(Runnable task) { + return executeAsync(task); + } + } diff --git a/common/version-control/pom.xml b/common/version-control/pom.xml index 998ae0879b..ac472c8e3c 100644 --- a/common/version-control/pom.xml +++ b/common/version-control/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.4.0-SNAPSHOT + 3.4.1-SNAPSHOT common org.thingsboard.common diff --git a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultClusterVersionControlService.java b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultClusterVersionControlService.java index 7d8e81d1c4..eb7a3d20e4 100644 --- a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultClusterVersionControlService.java +++ b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultClusterVersionControlService.java @@ -274,20 +274,20 @@ public class DefaultClusterVersionControlService extends TbApplicationEventListe var ids = vcService.listEntitiesAtVersion(ctx.getTenantId(), request.getVersionId(), path) .stream().skip(request.getOffset()).limit(request.getLimit()).collect(Collectors.toList()); if (!ids.isEmpty()) { - for (VersionedEntityInfo info : ids) { + for (int i = 0; i < ids.size(); i++){ + VersionedEntityInfo info = ids.get(i); var data = vcService.getFileContentAtCommit(ctx.getTenantId(), getRelativePath(info.getExternalId().getEntityType(), info.getExternalId().getId().toString()), request.getVersionId()); - Iterable dataChunks = StringUtils.split(data, msgChunkSize); - String chunkedMsgId = UUID.randomUUID().toString(); int chunksCount = Iterables.size(dataChunks); AtomicInteger chunkIndex = new AtomicInteger(); + int itemIdx = i; dataChunks.forEach(chunk -> { EntitiesContentResponseMsg.Builder response = EntitiesContentResponseMsg.newBuilder() .setItemsCount(ids.size()) + .setItemIdx(itemIdx) .setItem(EntityContentResponseMsg.newBuilder() .setData(chunk) - .setChunkedMsgId(chunkedMsgId) .setChunksCount(chunksCount) .setChunkIndex(chunkIndex.getAndIncrement()) .build()); @@ -313,7 +313,7 @@ public class DefaultClusterVersionControlService extends TbApplicationEventListe dataChunks.forEach(chunk -> { log.trace("[{}] sending chunk {} for 'getEntity'", chunkedMsgId, chunkIndex.get()); reply(ctx, Optional.empty(), builder -> builder.setEntityContentResponse(EntityContentResponseMsg.newBuilder() - .setData(chunk).setChunkedMsgId(chunkedMsgId).setChunksCount(chunksCount) + .setData(chunk).setChunksCount(chunksCount) .setChunkIndex(chunkIndex.getAndIncrement()))); }); } @@ -526,7 +526,8 @@ public class DefaultClusterVersionControlService extends TbApplicationEventListe .setRequestIdLSB(ctx.getRequestId().getLeastSignificantBits()); if (e.isPresent()) { log.debug("[{}][{}] Failed to process task", ctx.getTenantId(), ctx.getRequestId(), e.get()); - builder.setError(e.get().getMessage()); + var message = e.get().getMessage(); + builder.setError(message != null ? message : e.get().getClass().getSimpleName()); } else { if (enrichFunction != null) { builder = enrichFunction.apply(builder); diff --git a/dao/pom.xml b/dao/pom.xml index 1528a74869..ae98928787 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.4.0-SNAPSHOT + 3.4.1-SNAPSHOT thingsboard dao diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java index eb85f70687..31c40e0d96 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java @@ -22,6 +22,7 @@ import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; import org.hibernate.exception.ConstraintViolationException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.util.Pair; import org.springframework.stereotype.Service; import org.springframework.transaction.event.TransactionalEventListener; import org.thingsboard.server.common.data.EntitySubtype; @@ -126,12 +127,10 @@ public class BaseAssetService extends AbstractCachedEntityService extractConstraintViolationException(Exception t) { + protected static Optional extractConstraintViolationException(Exception t) { if (t instanceof ConstraintViolationException) { return Optional.of((ConstraintViolationException) t); } else if (t.getCause() instanceof ConstraintViolationException) { @@ -83,6 +87,29 @@ public abstract class AbstractEntityService { } } + public static final void checkConstraintViolation(Exception t, String constraintName, String constraintMessage) { + checkConstraintViolation(t, Collections.singletonMap(constraintName, constraintMessage)); + } + + public static final void checkConstraintViolation(Exception t, String constraintName1, String constraintMessage1, String constraintName2, String constraintMessage2) { + checkConstraintViolation(t, Map.of(constraintName1, constraintMessage1, constraintName2, constraintMessage2)); + } + + public static final void checkConstraintViolation(Exception t, Map constraints) { + var exOpt = extractConstraintViolationException(t); + if (exOpt.isPresent()) { + var ex = exOpt.get(); + if (StringUtils.isNotEmpty(ex.getConstraintName())) { + var constraintName = ex.getConstraintName(); + for (var constraintMessage : constraints.entrySet()) { + if (constraintName.equals(constraintMessage.getKey())) { + throw new DataValidationException(constraintMessage.getValue()); + } + } + } + } + } + protected void checkAssignedEntityViewsToEdge(TenantId tenantId, EntityId entityId, EdgeId edgeId) { List entityViews = entityViewService.findEntityViewsByTenantIdAndEntityId(tenantId, entityId); if (entityViews != null && !entityViews.isEmpty()) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java index 6b1d6775d6..f71f2cc7a9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java @@ -98,9 +98,15 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService impl @Column(name = ModelConstants.DEVICE_PROFILE_DEFAULT_DASHBOARD_ID_PROPERTY) private UUID defaultDashboardId; - @Column(name = ModelConstants.DEVICE_PROFILE_DEFAULT_QUEUE_ID_PROPERTY) - private UUID defaultQueueId; + @Column(name = ModelConstants.DEVICE_PROFILE_DEFAULT_QUEUE_NAME_PROPERTY) + private String defaultQueueName; @Type(type = "jsonb") @Column(name = ModelConstants.DEVICE_PROFILE_PROFILE_DATA_PROPERTY, columnDefinition = "jsonb") @@ -133,9 +133,7 @@ public final class DeviceProfileEntity extends BaseSqlEntity impl if (deviceProfile.getDefaultDashboardId() != null) { this.defaultDashboardId = deviceProfile.getDefaultDashboardId().getId(); } - if (deviceProfile.getDefaultQueueId() != null) { - this.defaultQueueId = deviceProfile.getDefaultQueueId().getId(); - } + this.defaultQueueName = deviceProfile.getDefaultQueueName(); this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey(); if (deviceProfile.getFirmwareId() != null) { this.firmwareId = deviceProfile.getFirmwareId().getId(); @@ -176,6 +174,7 @@ public final class DeviceProfileEntity extends BaseSqlEntity impl deviceProfile.setProvisionType(provisionType); deviceProfile.setDescription(description); deviceProfile.setDefault(isDefault); + deviceProfile.setDefaultQueueName(defaultQueueName); deviceProfile.setProfileData(JacksonUtil.convertValue(profileData, DeviceProfileData.class)); if (defaultRuleChainId != null) { deviceProfile.setDefaultRuleChainId(new RuleChainId(defaultRuleChainId)); @@ -183,15 +182,11 @@ public final class DeviceProfileEntity extends BaseSqlEntity impl if (defaultDashboardId != null) { deviceProfile.setDefaultDashboardId(new DashboardId(defaultDashboardId)); } - if (defaultQueueId != null) { - deviceProfile.setDefaultQueueId(new QueueId(defaultQueueId)); - } deviceProfile.setProvisionDeviceKey(provisionDeviceKey); if (firmwareId != null) { deviceProfile.setFirmwareId(new OtaPackageId(firmwareId)); } - if (softwareId != null) { deviceProfile.setSoftwareId(new OtaPackageId(softwareId)); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TenantProfileEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TenantProfileEntity.java index 6f7b094464..db584b9ff3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TenantProfileEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TenantProfileEntity.java @@ -53,9 +53,6 @@ public final class TenantProfileEntity extends BaseSqlEntity impl @Column(name = ModelConstants.TENANT_PROFILE_IS_DEFAULT_PROPERTY) private boolean isDefault; - @Column(name = ModelConstants.TENANT_PROFILE_ISOLATED_TB_CORE) - private boolean isolatedTbCore; - @Column(name = ModelConstants.TENANT_PROFILE_ISOLATED_TB_RULE_ENGINE) private boolean isolatedTbRuleEngine; @@ -75,7 +72,6 @@ public final class TenantProfileEntity extends BaseSqlEntity impl this.name = tenantProfile.getName(); this.description = tenantProfile.getDescription(); this.isDefault = tenantProfile.isDefault(); - this.isolatedTbCore = tenantProfile.isIsolatedTbCore(); this.isolatedTbRuleEngine = tenantProfile.isIsolatedTbRuleEngine(); this.profileData = JacksonUtil.convertValue(tenantProfile.getProfileData(), ObjectNode.class); } @@ -101,7 +97,6 @@ public final class TenantProfileEntity extends BaseSqlEntity impl tenantProfile.setName(name); tenantProfile.setDescription(description); tenantProfile.setDefault(isDefault); - tenantProfile.setIsolatedTbCore(isolatedTbCore); tenantProfile.setIsolatedTbRuleEngine(isolatedTbRuleEngine); tenantProfile.setProfileData(JacksonUtil.convertValue(profileData, TenantProfileData.class)); return tenantProfile; diff --git a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java index 1587aa9f52..841ff82441 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java @@ -234,14 +234,4 @@ public class BaseOtaPackageService extends AbstractCachedEntityService extractConstraintViolationException(Exception t) { - if (t instanceof ConstraintViolationException) { - return Optional.of((ConstraintViolationException) t); - } else if (t.getCause() instanceof ConstraintViolationException) { - return Optional.of((ConstraintViolationException) (t.getCause())); - } else { - return Optional.empty(); - } - } - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java index 28aa798c53..b511ab3da2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java @@ -84,7 +84,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC private static final int DEFAULT_PAGE_SIZE = 1000; public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; - + public static final String TB_RULE_CHAIN_INPUT_NODE = "org.thingsboard.rule.engine.flow.TbRuleChainInputNode"; @Autowired private RuleChainDao ruleChainDao; @@ -98,7 +98,12 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC @Transactional public RuleChain saveRuleChain(RuleChain ruleChain) { ruleChainValidator.validate(ruleChain, RuleChain::getTenantId); - return ruleChainDao.save(ruleChain.getTenantId(), ruleChain); + try { + return ruleChainDao.save(ruleChain.getTenantId(), ruleChain); + } catch (Exception e) { + checkConstraintViolation(e, "rule_chain_external_id_unq_key", "Rule Chain with such external id already exists!"); + throw e; + } } @Override @@ -566,6 +571,19 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC } } } + if (!CollectionUtils.isEmpty(metaData.getNodes())) { + metaData.getNodes().stream() + .filter(ruleNode -> ruleNode.getType().equals(TB_RULE_CHAIN_INPUT_NODE)) + .forEach(ruleNode -> { + ObjectNode configuration = (ObjectNode) ruleNode.getConfiguration(); + if (configuration.has("ruleChainId")) { + if (configuration.get("ruleChainId").asText().equals(oldRuleChainId.toString())) { + configuration.put("ruleChainId", newRuleChainId.toString()); + ruleNode.setConfiguration(configuration); + } + } + }); + } } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AbstractHasOtaPackageValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AbstractHasOtaPackageValidator.java new file mode 100644 index 0000000000..069e1fcfc8 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AbstractHasOtaPackageValidator.java @@ -0,0 +1,68 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.dao.service.validator; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.HasOtaPackage; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.ota.OtaPackageService; +import org.thingsboard.server.dao.service.DataValidator; + +public abstract class AbstractHasOtaPackageValidator> extends DataValidator { + + @Autowired + @Lazy + private OtaPackageService otaPackageService; + + protected void validateOtaPackage(TenantId tenantId, T entity, DeviceProfileId deviceProfileId) { + if (entity.getFirmwareId() != null) { + OtaPackage firmware = otaPackageService.findOtaPackageById(tenantId, entity.getFirmwareId()); + validateOtaPackage(tenantId, OtaPackageType.FIRMWARE, deviceProfileId, firmware); + } + if (entity.getSoftwareId() != null) { + OtaPackage software = otaPackageService.findOtaPackageById(tenantId, entity.getSoftwareId()); + validateOtaPackage(tenantId, OtaPackageType.SOFTWARE, deviceProfileId, software); + } + } + + private void validateOtaPackage(TenantId tenantId, OtaPackageType type, DeviceProfileId deviceProfileId, OtaPackage otaPackage) { + if (otaPackage == null) { + throw new DataValidationException(prepareMsg("Can't assign non-existent %s!", type)); + } + if (!otaPackage.getTenantId().equals(tenantId)) { + throw new DataValidationException(prepareMsg("Can't assign %s from different tenant!", type)); + } + if (!otaPackage.getType().equals(type)) { + throw new DataValidationException(prepareMsg("Can't assign %s with type: " + otaPackage.getType(), type)); + } + if (otaPackage.getData() == null && !otaPackage.hasUrl()) { + throw new DataValidationException(prepareMsg("Can't assign %s with empty data!", type)); + } + if (!otaPackage.getDeviceProfileId().equals(deviceProfileId)) { + throw new DataValidationException(prepareMsg("Can't assign %s with different deviceProfile!", type)); + } + } + + private String prepareMsg(String msg, OtaPackageType type) { + return String.format(msg, type.name().toLowerCase()); + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/BaseOtaPackageDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/BaseOtaPackageDataValidator.java index c7ffe8c61e..f0c7c3e15d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/BaseOtaPackageDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/BaseOtaPackageDataValidator.java @@ -16,6 +16,7 @@ package org.thingsboard.server.dao.service.validator; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.OtaPackageInfo; @@ -30,6 +31,7 @@ import java.util.Objects; public abstract class BaseOtaPackageDataValidator> extends DataValidator { @Autowired + @Lazy private TenantService tenantService; @Autowired diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceDataValidator.java index 23e520f070..d30180a892 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceDataValidator.java @@ -22,17 +22,13 @@ import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.device.data.DeviceTransportConfiguration; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.customer.CustomerDao; import org.thingsboard.server.dao.device.DeviceDao; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.ota.OtaPackageService; -import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantService; @@ -41,7 +37,7 @@ import java.util.Optional; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; @Component -public class DeviceDataValidator extends DataValidator { +public class DeviceDataValidator extends AbstractHasOtaPackageValidator { @Autowired private DeviceDao deviceDao; @@ -56,9 +52,6 @@ public class DeviceDataValidator extends DataValidator { @Lazy private TbTenantProfileCache tenantProfileCache; - @Autowired - private OtaPackageService otaPackageService; - @Override protected void validateCreate(TenantId tenantId, Device device) { DefaultTenantProfileConfiguration profileConfiguration = @@ -103,36 +96,6 @@ public class DeviceDataValidator extends DataValidator { .flatMap(deviceData -> Optional.ofNullable(deviceData.getTransportConfiguration())) .ifPresent(DeviceTransportConfiguration::validate); - if (device.getFirmwareId() != null) { - OtaPackage firmware = otaPackageService.findOtaPackageById(tenantId, device.getFirmwareId()); - if (firmware == null) { - throw new DataValidationException("Can't assign non-existent firmware!"); - } - if (!firmware.getType().equals(OtaPackageType.FIRMWARE)) { - throw new DataValidationException("Can't assign firmware with type: " + firmware.getType()); - } - if (firmware.getData() == null && !firmware.hasUrl()) { - throw new DataValidationException("Can't assign firmware with empty data!"); - } - if (!firmware.getDeviceProfileId().equals(device.getDeviceProfileId())) { - throw new DataValidationException("Can't assign firmware with different deviceProfile!"); - } - } - - if (device.getSoftwareId() != null) { - OtaPackage software = otaPackageService.findOtaPackageById(tenantId, device.getSoftwareId()); - if (software == null) { - throw new DataValidationException("Can't assign non-existent software!"); - } - if (!software.getType().equals(OtaPackageType.SOFTWARE)) { - throw new DataValidationException("Can't assign software with type: " + software.getType()); - } - if (software.getData() == null && !software.hasUrl()) { - throw new DataValidationException("Can't assign software with empty data!"); - } - if (!software.getDeviceProfileId().equals(device.getDeviceProfileId())) { - throw new DataValidationException("Can't assign firmware with different deviceProfile!"); - } - } + validateOtaPackage(tenantId, device, device.getDeviceProfileId()); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java index b1fb9b150b..c767c3cbcd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java @@ -35,8 +35,8 @@ import org.springframework.util.CollectionUtils; import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfileProvisionType; -import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode; import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.CoapDeviceTypeConfiguration; @@ -52,7 +52,6 @@ import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBo import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.RPKLwM2MBootstrapServerCredential; import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.X509LwM2MBootstrapServerCredential; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.msg.EncryptionUtil; @@ -62,10 +61,9 @@ import org.thingsboard.server.dao.device.DeviceProfileDao; import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; -import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.rule.RuleChainService; -import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantService; import java.util.HashSet; @@ -74,7 +72,7 @@ import java.util.Set; import java.util.stream.Collectors; @Component -public class DeviceProfileDataValidator extends DataValidator { +public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator { private static final Location LOCATION = new Location("", "", -1, -1); private static final String ATTRIBUTES_PROTO_SCHEMA = "attributes proto schema"; @@ -94,11 +92,11 @@ public class DeviceProfileDataValidator extends DataValidator { @Autowired private QueueService queueService; @Autowired - private OtaPackageService otaPackageService; - @Autowired private RuleChainService ruleChainService; @Autowired private DashboardService dashboardService; + @Autowired + private TbTenantProfileCache tenantProfileCache; private static String invalidSchemaProvidedMessage(String schemaName) { return "[Transport Configuration] invalid " + schemaName + " provided!"; @@ -128,8 +126,8 @@ public class DeviceProfileDataValidator extends DataValidator { throw new DataValidationException("Another default device profile is present in scope of current tenant!"); } } - if (deviceProfile.getDefaultQueueId() != null) { - Queue queue = queueService.findQueueById(tenantId, deviceProfile.getDefaultQueueId()); + if (StringUtils.isNotEmpty(deviceProfile.getDefaultQueueName())) { + Queue queue = queueService.findQueueByTenantIdAndName(tenantId, deviceProfile.getDefaultQueueName()); if (queue == null) { throw new DataValidationException("Device profile is referencing to non-existent queue!"); } @@ -192,6 +190,9 @@ public class DeviceProfileDataValidator extends DataValidator { if (ruleChain == null) { throw new DataValidationException("Can't assign non-existent rule chain!"); } + if (!ruleChain.getTenantId().equals(deviceProfile.getTenantId())) { + throw new DataValidationException("Can't assign rule chain from different tenant!"); + } } if (deviceProfile.getDefaultDashboardId() != null) { @@ -199,39 +200,12 @@ public class DeviceProfileDataValidator extends DataValidator { if (dashboard == null) { throw new DataValidationException("Can't assign non-existent dashboard!"); } - } - - if (deviceProfile.getFirmwareId() != null) { - OtaPackage firmware = otaPackageService.findOtaPackageById(tenantId, deviceProfile.getFirmwareId()); - if (firmware == null) { - throw new DataValidationException("Can't assign non-existent firmware!"); - } - if (!firmware.getType().equals(OtaPackageType.FIRMWARE)) { - throw new DataValidationException("Can't assign firmware with type: " + firmware.getType()); - } - if (firmware.getData() == null && !firmware.hasUrl()) { - throw new DataValidationException("Can't assign firmware with empty data!"); - } - if (!firmware.getDeviceProfileId().equals(deviceProfile.getId())) { - throw new DataValidationException("Can't assign firmware with different deviceProfile!"); + if (!dashboard.getTenantId().equals(deviceProfile.getTenantId())) { + throw new DataValidationException("Can't assign dashboard from different tenant!"); } } - if (deviceProfile.getSoftwareId() != null) { - OtaPackage software = otaPackageService.findOtaPackageById(tenantId, deviceProfile.getSoftwareId()); - if (software == null) { - throw new DataValidationException("Can't assign non-existent software!"); - } - if (!software.getType().equals(OtaPackageType.SOFTWARE)) { - throw new DataValidationException("Can't assign software with type: " + software.getType()); - } - if (software.getData() == null && !software.hasUrl()) { - throw new DataValidationException("Can't assign software with empty data!"); - } - if (!software.getDeviceProfileId().equals(deviceProfile.getId())) { - throw new DataValidationException("Can't assign firmware with different deviceProfile!"); - } - } + validateOtaPackage(tenantId, deviceProfile, deviceProfile.getId()); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java index feab5078e6..8484ecfa59 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java @@ -100,8 +100,6 @@ public class TenantProfileDataValidator extends DataValidator { throw new DataValidationException("Can't update non existing tenant profile!"); } else if (old.isIsolatedTbRuleEngine() != tenantProfile.isIsolatedTbRuleEngine()) { throw new DataValidationException("Can't update isolatedTbRuleEngine property!"); - } else if (old.isIsolatedTbCore() != tenantProfile.isIsolatedTbCore()) { - throw new DataValidationException("Can't update isolatedTbCore property!"); } return old; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java index 34dfcab964..8d6af88c59 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java @@ -543,6 +543,8 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { ctx.addUuidParameter("permissions_customer_id", ctx.getCustomerId().getId()); if (ctx.getEntityType() == EntityType.CUSTOMER) { return "e.tenant_id=:permissions_tenant_id and e.id=:permissions_customer_id"; + } else if (ctx.getEntityType() == EntityType.API_USAGE_STATE) { + return "e.tenant_id=:permissions_tenant_id and e.entity_id=:permissions_customer_id"; } else { return "e.tenant_id=:permissions_tenant_id and e.customer_id=:permissions_customer_id"; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java index 71140140c7..8a2d3354d4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java @@ -153,7 +153,6 @@ public class TenantProfileServiceImpl extends AbstractCachedEntityService list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0L, + 8000000L, 200000, 3, Aggregation.NONE))).get(MAX_TIMEOUT, TimeUnit.SECONDS); + assertEquals(3, list.size()); + + tsService.remove(tenantId, deviceId, Collections.singletonList( + new BaseDeleteTsKvQuery(LONG_KEY, 0L, 8000000L, false))).get(MAX_TIMEOUT, TimeUnit.SECONDS); + list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0L, + 8000000L, 200000, 3, Aggregation.NONE))).get(MAX_TIMEOUT, TimeUnit.SECONDS); + assertEquals(0, list.size()); + + save(deviceId, 2000000L, 99); + save(deviceId, 4000000L, 104); + save(deviceId, 6000000L, 109); + list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0L, + 8000000L, 200000, 3, Aggregation.NONE))).get(MAX_TIMEOUT, TimeUnit.SECONDS); + assertEquals(3, list.size()); + } + private TsKvEntry save(DeviceId deviceId, long ts, long value) throws Exception { TsKvEntry entry = new BasicTsKvEntry(ts, new LongDataEntry(LONG_KEY, value)); tsService.save(tenantId, deviceId, entry).get(MAX_TIMEOUT, TimeUnit.SECONDS); diff --git a/dao/src/test/resources/application-test.properties b/dao/src/test/resources/application-test.properties index bf4178ec44..27f82f331c 100644 --- a/dao/src/test/resources/application-test.properties +++ b/dao/src/test/resources/application-test.properties @@ -5,8 +5,6 @@ zk.zk_dir=/thingsboard updates.enabled=false audit-log.enabled=true -audit-log.by_tenant_partitioning=MONTHS -audit-log.default_query_period=30 audit-log.sink.type=none cache.type=caffeine diff --git a/docker/.env b/docker/.env index 7bc1a7a6a4..f33ed50da5 100644 --- a/docker/.env +++ b/docker/.env @@ -25,4 +25,7 @@ DATABASE=postgres LOAD_BALANCER_NAME=haproxy-certbot # If enabled Prometheus and Grafana containers are deployed along with other containers -MONITORING_ENABLED=false \ No newline at end of file +MONITORING_ENABLED=false + +# Limit memory usage for each Java application +# JAVA_OPTS=-Xmx2048M -Xms2048M -Xss384k -XX:+AlwaysPreTouch diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 3e0f21391d..6ec410fcde 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -48,6 +48,7 @@ services: TB_SERVICE_ID: tb-core1 TB_SERVICE_TYPE: tb-core EDGES_ENABLED: "true" + JAVA_OPTS: "${JAVA_OPTS}" env_file: - tb-node.env volumes: @@ -73,6 +74,7 @@ services: TB_SERVICE_ID: tb-core2 TB_SERVICE_TYPE: tb-core EDGES_ENABLED: "true" + JAVA_OPTS: "${JAVA_OPTS}" env_file: - tb-node.env volumes: @@ -96,6 +98,7 @@ services: environment: TB_SERVICE_ID: tb-rule-engine1 TB_SERVICE_TYPE: tb-rule-engine + JAVA_OPTS: "${JAVA_OPTS}" env_file: - tb-node.env volumes: @@ -117,6 +120,7 @@ services: environment: TB_SERVICE_ID: tb-rule-engine2 TB_SERVICE_TYPE: tb-rule-engine + JAVA_OPTS: "${JAVA_OPTS}" env_file: - tb-node.env volumes: @@ -132,6 +136,7 @@ services: - "1883" environment: TB_SERVICE_ID: tb-mqtt-transport1 + JAVA_OPTS: "${JAVA_OPTS}" env_file: - tb-mqtt-transport.env volumes: @@ -148,6 +153,7 @@ services: - "1883" environment: TB_SERVICE_ID: tb-mqtt-transport2 + JAVA_OPTS: "${JAVA_OPTS}" env_file: - tb-mqtt-transport.env volumes: @@ -164,6 +170,7 @@ services: - "8081" environment: TB_SERVICE_ID: tb-http-transport1 + JAVA_OPTS: "${JAVA_OPTS}" env_file: - tb-http-transport.env volumes: @@ -180,6 +187,7 @@ services: - "8081" environment: TB_SERVICE_ID: tb-http-transport2 + JAVA_OPTS: "${JAVA_OPTS}" env_file: - tb-http-transport.env volumes: @@ -196,6 +204,7 @@ services: - "5683:5683/udp" environment: TB_SERVICE_ID: tb-coap-transport + JAVA_OPTS: "${JAVA_OPTS}" env_file: - tb-coap-transport.env volumes: @@ -212,6 +221,7 @@ services: - "5685:5685/udp" environment: TB_SERVICE_ID: tb-lwm2m-transport + JAVA_OPTS: "${JAVA_OPTS}" env_file: - tb-lwm2m-transport.env volumes: @@ -226,6 +236,7 @@ services: image: "${DOCKER_REPO}/${SNMP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}" environment: TB_SERVICE_ID: tb-snmp-transport + JAVA_OPTS: "${JAVA_OPTS}" env_file: - tb-snmp-transport.env volumes: @@ -256,6 +267,7 @@ services: - "8081" environment: TB_SERVICE_ID: tb-vc-executor1 + JAVA_OPTS: "${JAVA_OPTS}" env_file: - tb-vc-executor.env volumes: @@ -272,6 +284,7 @@ services: - "8081" environment: TB_SERVICE_ID: tb-vc-executor2 + JAVA_OPTS: "${JAVA_OPTS}" env_file: - tb-vc-executor.env volumes: diff --git a/docker/tb-node/conf/logback.xml b/docker/tb-node/conf/logback.xml index bc694d704d..c17e8a43cf 100644 --- a/docker/tb-node/conf/logback.xml +++ b/docker/tb-node/conf/logback.xml @@ -42,6 +42,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docker/tb-transports/coap/conf/logback.xml b/docker/tb-transports/coap/conf/logback.xml index 1a3e8ef6b3..65d13a55cf 100644 --- a/docker/tb-transports/coap/conf/logback.xml +++ b/docker/tb-transports/coap/conf/logback.xml @@ -42,6 +42,11 @@ + + + + + diff --git a/docker/tb-transports/http/conf/logback.xml b/docker/tb-transports/http/conf/logback.xml index 05361fe680..a3ae374bb7 100644 --- a/docker/tb-transports/http/conf/logback.xml +++ b/docker/tb-transports/http/conf/logback.xml @@ -42,6 +42,11 @@ + + + + + diff --git a/docker/tb-transports/lwm2m/conf/logback.xml b/docker/tb-transports/lwm2m/conf/logback.xml index d2485675ee..5546167f3d 100644 --- a/docker/tb-transports/lwm2m/conf/logback.xml +++ b/docker/tb-transports/lwm2m/conf/logback.xml @@ -42,6 +42,10 @@ + + + + diff --git a/docker/tb-transports/mqtt/conf/logback.xml b/docker/tb-transports/mqtt/conf/logback.xml index 09bcaea84c..d4e7b811f8 100644 --- a/docker/tb-transports/mqtt/conf/logback.xml +++ b/docker/tb-transports/mqtt/conf/logback.xml @@ -42,6 +42,10 @@ + + + + diff --git a/docker/tb-transports/snmp/conf/logback.xml b/docker/tb-transports/snmp/conf/logback.xml index dc53ddd8a3..0b6fbc726b 100644 --- a/docker/tb-transports/snmp/conf/logback.xml +++ b/docker/tb-transports/snmp/conf/logback.xml @@ -42,6 +42,10 @@ + + + + diff --git a/docker/tb-vc-executor/conf/logback.xml b/docker/tb-vc-executor/conf/logback.xml index dc95b3a885..ebde7cbd69 100644 --- a/docker/tb-vc-executor/conf/logback.xml +++ b/docker/tb-vc-executor/conf/logback.xml @@ -40,8 +40,11 @@ - - + + + + + diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml index 29279b0bed..da4e900015 100644 --- a/msa/black-box-tests/pom.xml +++ b/msa/black-box-tests/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.4.0-SNAPSHOT + 3.4.1-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java index 5ea04de462..bfe1c913eb 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java @@ -69,6 +69,9 @@ public abstract class AbstractContainerTest { protected static final String WSS_URL = "wss://localhost"; protected static String TB_TOKEN; protected static RestClient restClient; + + protected static long timeoutMultiplier = 1; + protected ObjectMapper mapper = new ObjectMapper(); protected JsonParser jsonParser = new JsonParser(); @@ -76,6 +79,10 @@ public abstract class AbstractContainerTest { public static void before() throws Exception { restClient = new RestClient(HTTPS_URL); restClient.getRestTemplate().setRequestFactory(getRequestFactoryForSelfSignedCert()); + + if (!"kafka".equals(System.getProperty("blackBoxTests.queue", "kafka"))) { + timeoutMultiplier = 10; + } } @Rule @@ -124,7 +131,7 @@ public abstract class AbstractContainerTest { } protected WsClient subscribeToWebSocket(DeviceId deviceId, String scope, CmdsType property) throws Exception { - WsClient wsClient = new WsClient(new URI(WSS_URL + "/api/ws/plugins/telemetry?token=" + restClient.getToken())); + WsClient wsClient = new WsClient(new URI(WSS_URL + "/api/ws/plugins/telemetry?token=" + restClient.getToken()), timeoutMultiplier); SSLContextBuilder builder = SSLContexts.custom(); builder.loadTrustMaterial(null, (TrustStrategy) (chain, authType) -> true); wsClient.setSocketFactory(builder.build().getSocketFactory()); diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ContainerTestSuite.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ContainerTestSuite.java index 1fbf3755e6..47acbde16e 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ContainerTestSuite.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ContainerTestSuite.java @@ -22,14 +22,19 @@ import org.junit.extensions.cpsuite.ClasspathSuite; import org.junit.runner.RunWith; import org.testcontainers.containers.DockerComposeContainer; import org.testcontainers.containers.wait.strategy.Wait; +import org.thingsboard.server.common.data.StringUtils; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; import static org.hamcrest.CoreMatchers.containsString; @@ -44,10 +49,13 @@ import static org.junit.Assert.fail; public class ContainerTestSuite { final static boolean IS_REDIS_CLUSTER = Boolean.parseBoolean(System.getProperty("blackBoxTests.redisCluster")); final static boolean IS_HYBRID_MODE = Boolean.parseBoolean(System.getProperty("blackBoxTests.hybridMode")); + final static String QUEUE_TYPE = System.getProperty("blackBoxTests.queue", "kafka"); private static final String SOURCE_DIR = "./../../docker/"; private static final String TB_CORE_LOG_REGEXP = ".*Starting polling for events.*"; private static final String TRANSPORTS_LOG_REGEXP = ".*Going to recalculate partitions.*"; private static final String TB_VC_LOG_REGEXP = TRANSPORTS_LOG_REGEXP; + private static final String TB_JS_EXECUTOR_LOG_REGEXP = ".*template started.*"; + private static final Duration CONTAINER_STARTUP_TIMEOUT = Duration.ofSeconds(400); private static DockerComposeContainer testContainer; @@ -66,6 +74,8 @@ public class ContainerTestSuite { FileUtils.copyDirectory(new File(SOURCE_DIR), new File(targetDir)); replaceInFile(targetDir + "docker-compose.yml", " container_name: \"${LOAD_BALANCER_NAME}\"", "", "container_name"); + FileUtils.copyDirectory(new File("src/test/resources"), new File(targetDir)); + class DockerComposeContainerImpl> extends DockerComposeContainer { public DockerComposeContainerImpl(List composeFiles) { super(composeFiles); @@ -81,17 +91,45 @@ public class ContainerTestSuite { List composeFiles = new ArrayList<>(Arrays.asList( new File(targetDir + "docker-compose.yml"), new File(targetDir + "docker-compose.volumes.yml"), - IS_HYBRID_MODE - ? new File(targetDir + "docker-compose.hybrid.yml") - : new File(targetDir + "docker-compose.postgres.yml"), + new File(targetDir + (IS_HYBRID_MODE ? "docker-compose.hybrid.yml" : "docker-compose.postgres.yml")), new File(targetDir + "docker-compose.postgres.volumes.yml"), - new File(targetDir + "docker-compose.kafka.yml"), - IS_REDIS_CLUSTER - ? new File(targetDir + "docker-compose.redis-cluster.yml") - : new File(targetDir + "docker-compose.redis.yml"), - IS_REDIS_CLUSTER - ? new File(targetDir + "docker-compose.redis-cluster.volumes.yml") - : new File(targetDir + "docker-compose.redis.volumes.yml"))); + new File(targetDir + "docker-compose." + QUEUE_TYPE + ".yml"), + new File(targetDir + (IS_REDIS_CLUSTER ? "docker-compose.redis-cluster.yml" : "docker-compose.redis.yml")), + new File(targetDir + (IS_REDIS_CLUSTER ? "docker-compose.redis-cluster.volumes.yml" : "docker-compose.redis.volumes.yml")) + )); + + Map queueEnv = new HashMap<>(); + queueEnv.put("TB_QUEUE_TYPE", QUEUE_TYPE); + switch (QUEUE_TYPE) { + case "kafka": + composeFiles.add(new File(targetDir + "docker-compose.kafka.yml")); + break; + case "aws-sqs": + replaceInFile(targetDir, "queue-aws-sqs.env", + Map.of("YOUR_KEY", getSysProp("blackBoxTests.awsKey"), + "YOUR_SECRET", getSysProp("blackBoxTests.awsSecret"), + "YOUR_REGION", getSysProp("blackBoxTests.awsRegion"))); + break; + case "rabbitmq": + composeFiles.add(new File(targetDir + "docker-compose.rabbitmq-server.yml")); + replaceInFile(targetDir, "queue-rabbitmq.env", + Map.of("localhost", "rabbitmq")); + break; + case "service-bus": + replaceInFile(targetDir, "queue-service-bus.env", + Map.of("YOUR_NAMESPACE_NAME", getSysProp("blackBoxTests.serviceBusNamespace"), + "YOUR_SAS_KEY_NAME", getSysProp("blackBoxTests.serviceBusSASPolicy"))); + replaceInFile(targetDir, "queue-service-bus.env", + Map.of("YOUR_SAS_KEY", getSysProp("blackBoxTests.serviceBusPrimaryKey"))); + break; + case "pubsub": + replaceInFile(targetDir, "queue-pubsub.env", + Map.of("YOUR_PROJECT_ID", getSysProp("blackBoxTests.pubSubProjectId"), + "YOUR_SERVICE_ACCOUNT", getSysProp("blackBoxTests.pubSubServiceAccount"))); + break; + default: + throw new RuntimeException("Unsupported queue type: " + QUEUE_TYPE); + } if (IS_HYBRID_MODE) { composeFiles.add(new File(targetDir + "docker-compose.cassandra.volumes.yml")); @@ -102,16 +140,18 @@ public class ContainerTestSuite { .withLocalCompose(true) .withTailChildContainers(!skipTailChildContainers) .withEnv(installTb.getEnv()) + .withEnv(queueEnv) .withEnv("LOAD_BALANCER_NAME", "") - .withExposedService("haproxy", 80, Wait.forHttp("/swagger-ui.html").withStartupTimeout(Duration.ofSeconds(400))) - .waitingFor("tb-core1", Wait.forLogMessage(TB_CORE_LOG_REGEXP, 1).withStartupTimeout(Duration.ofSeconds(400))) - .waitingFor("tb-core2", Wait.forLogMessage(TB_CORE_LOG_REGEXP, 1).withStartupTimeout(Duration.ofSeconds(400))) - .waitingFor("tb-http-transport1", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(Duration.ofSeconds(400))) - .waitingFor("tb-http-transport2", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(Duration.ofSeconds(400))) - .waitingFor("tb-mqtt-transport1", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(Duration.ofSeconds(400))) - .waitingFor("tb-mqtt-transport2", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(Duration.ofSeconds(400))) - .waitingFor("tb-vc-executor1", Wait.forLogMessage(TB_VC_LOG_REGEXP, 1).withStartupTimeout(Duration.ofSeconds(400))) - .waitingFor("tb-vc-executor2", Wait.forLogMessage(TB_VC_LOG_REGEXP, 1).withStartupTimeout(Duration.ofSeconds(400))); + .withExposedService("haproxy", 80, Wait.forHttp("/swagger-ui.html").withStartupTimeout(CONTAINER_STARTUP_TIMEOUT)) + .waitingFor("tb-core1", Wait.forLogMessage(TB_CORE_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT)) + .waitingFor("tb-core2", Wait.forLogMessage(TB_CORE_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT)) + .waitingFor("tb-http-transport1", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT)) + .waitingFor("tb-http-transport2", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT)) + .waitingFor("tb-mqtt-transport1", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT)) + .waitingFor("tb-mqtt-transport2", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT)) + .waitingFor("tb-vc-executor1", Wait.forLogMessage(TB_VC_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT)) + .waitingFor("tb-vc-executor2", Wait.forLogMessage(TB_VC_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT)) + .waitingFor("tb-js-executor", Wait.forLogMessage(TB_JS_EXECUTOR_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT)); } catch (Exception e) { log.error("Failed to create test container", e); fail("Failed to create test container"); @@ -120,6 +160,23 @@ public class ContainerTestSuite { return testContainer; } + private static void replaceInFile(String targetDir, String fileName, Map replacements) throws IOException { + Path envFilePath = Path.of(targetDir, fileName); + String data = Files.readString(envFilePath); + for (var entry : replacements.entrySet()) { + data = data.replace(entry.getKey(), entry.getValue()); + } + Files.write(envFilePath, data.getBytes(StandardCharsets.UTF_8)); + } + + private static String getSysProp(String propertyName) { + var value = System.getProperty(propertyName); + if (StringUtils.isEmpty(value)) { + throw new RuntimeException("Please define system property: " + propertyName + "!"); + } + return value; + } + private static void tryDeleteDir(String targetDir) { try { log.info("Trying to delete temp dir {}", targetDir); @@ -135,7 +192,7 @@ public class ContainerTestSuite { * docker-compose files which contain container_name are not supported and the creation of DockerComposeContainer fails due to IllegalStateException. * This has been introduced in #1151 as a quick fix for unintuitive feedback. https://github.com/testcontainers/testcontainers-java/issues/1151 * Using the latest testcontainers and waiting for the fix... - * */ + */ private static void replaceInFile(String sourceFilename, String target, String replacement, String verifyPhrase) { try { File file = new File(sourceFilename); diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ThingsBoardDbInstaller.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ThingsBoardDbInstaller.java index b627d2787a..eedf070fac 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ThingsBoardDbInstaller.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ThingsBoardDbInstaller.java @@ -46,6 +46,7 @@ public class ThingsBoardDbInstaller extends ExternalResource { private final static String TB_MQTT_TRANSPORT_LOG_VOLUME = "tb-mqtt-transport-log-test-volume"; private final static String TB_SNMP_TRANSPORT_LOG_VOLUME = "tb-snmp-transport-log-test-volume"; private final static String TB_VC_EXECUTOR_LOG_VOLUME = "tb-vc-executor-log-test-volume"; + private final static String JAVA_OPTS = "-Xmx512m"; private final DockerComposeExecutor dockerCompose; @@ -102,6 +103,7 @@ public class ThingsBoardDbInstaller extends ExternalResource { dockerCompose = new DockerComposeExecutor(composeFiles, project); env = new HashMap<>(); + env.put("JAVA_OPTS", JAVA_OPTS); env.put("POSTGRES_DATA_VOLUME", postgresDataVolume); if (IS_HYBRID_MODE) { env.put("CASSANDRA_DATA_VOLUME", cassandraDataVolume); diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/WsClient.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/WsClient.java index 547ebaf575..d4ee31d9be 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/WsClient.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/WsClient.java @@ -36,8 +36,11 @@ public class WsClient extends WebSocketClient { private CountDownLatch firstReply = new CountDownLatch(1); private CountDownLatch latch = new CountDownLatch(1); - WsClient(URI serverUri) { + private final long timeoutMultiplier; + + WsClient(URI serverUri, long timeoutMultiplier) { super(serverUri); + this.timeoutMultiplier = timeoutMultiplier; } @Override @@ -74,8 +77,13 @@ public class WsClient extends WebSocketClient { public WsTelemetryResponse getLastMessage() { try { - latch.await(10, TimeUnit.SECONDS); - return this.message; + boolean result = latch.await(10 * timeoutMultiplier, TimeUnit.SECONDS); + if (result) { + return this.message; + } else { + log.error("Timeout, ws message wasn't received"); + throw new RuntimeException("Timeout, ws message wasn't received"); + } } catch (InterruptedException e) { log.error("Timeout, ws message wasn't received"); } @@ -84,7 +92,11 @@ public class WsClient extends WebSocketClient { void waitForFirstReply() { try { - firstReply.await(10, TimeUnit.SECONDS); + boolean result = firstReply.await(10 * timeoutMultiplier, TimeUnit.SECONDS); + if (!result) { + log.error("Timeout, ws message wasn't received"); + throw new RuntimeException("Timeout, ws message wasn't received"); + } } catch (InterruptedException e) { log.error("Timeout, ws message wasn't received"); throw new RuntimeException(e); diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/HttpClientTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/HttpClientTest.java index a774888af5..f81d03b394 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/HttpClientTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/HttpClientTest.java @@ -90,7 +90,7 @@ public class HttpClientTest extends AbstractContainerTest { Assert.assertTrue(deviceClientsAttributes.getStatusCode().is2xxSuccessful()); - TimeUnit.SECONDS.sleep(3); + TimeUnit.SECONDS.sleep(3 * timeoutMultiplier); @SuppressWarnings("deprecation") Optional allOptional = restClient.getAttributes(accessToken, null, null); diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttClientTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttClientTest.java index 336d75bb45..19a53cb1ce 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttClientTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttClientTest.java @@ -28,9 +28,6 @@ import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.RandomStringUtils; import org.junit.*; -import org.junit.rules.TestRule; -import org.junit.rules.TestWatcher; -import org.junit.runner.Description; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; @@ -181,14 +178,14 @@ public class MqttClientTest extends AbstractContainerTest { mqttClient.on("v1/devices/me/attributes/response/+", listener, MqttQoS.AT_LEAST_ONCE).get(); // Wait until subscription is processed - TimeUnit.SECONDS.sleep(3); + TimeUnit.SECONDS.sleep(3 * timeoutMultiplier); // Request attributes JsonObject request = new JsonObject(); request.addProperty("clientKeys", "clientAttr"); request.addProperty("sharedKeys", "sharedAttr"); mqttClient.publish("v1/devices/me/attributes/request/" + new Random().nextInt(100), Unpooled.wrappedBuffer(request.toString().getBytes())).get(); - MqttEvent event = listener.getEvents().poll(10, TimeUnit.SECONDS); + MqttEvent event = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS); AttributesResponse attributes = mapper.readValue(Objects.requireNonNull(event).getMessage(), AttributesResponse.class); log.info("Received telemetry: {}", attributes); @@ -212,7 +209,7 @@ public class MqttClientTest extends AbstractContainerTest { mqttClient.on("v1/devices/me/attributes", listener, MqttQoS.AT_LEAST_ONCE).get(); // Wait until subscription is processed - TimeUnit.SECONDS.sleep(3); + TimeUnit.SECONDS.sleep(3 * timeoutMultiplier); String sharedAttributeName = "sharedAttr"; @@ -226,7 +223,7 @@ public class MqttClientTest extends AbstractContainerTest { device.getId()); Assert.assertTrue(sharedAttributesResponse.getStatusCode().is2xxSuccessful()); - MqttEvent event = listener.getEvents().poll(10, TimeUnit.SECONDS); + MqttEvent event = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS); Assert.assertEquals(sharedAttributeValue, mapper.readValue(Objects.requireNonNull(event).getMessage(), JsonNode.class).get(sharedAttributeName).asText()); @@ -240,7 +237,7 @@ public class MqttClientTest extends AbstractContainerTest { device.getId()); Assert.assertTrue(updatedSharedAttributesResponse.getStatusCode().is2xxSuccessful()); - event = listener.getEvents().poll(10, TimeUnit.SECONDS); + event = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS); Assert.assertEquals(updatedSharedAttributeValue, mapper.readValue(Objects.requireNonNull(event).getMessage(), JsonNode.class).get(sharedAttributeName).asText()); @@ -258,7 +255,7 @@ public class MqttClientTest extends AbstractContainerTest { mqttClient.on("v1/devices/me/rpc/request/+", listener, MqttQoS.AT_LEAST_ONCE).get(); // Wait until subscription is processed - TimeUnit.SECONDS.sleep(3); + TimeUnit.SECONDS.sleep(3 * timeoutMultiplier); // Send an RPC from the server JsonObject serverRpcPayload = new JsonObject(); @@ -277,7 +274,7 @@ public class MqttClientTest extends AbstractContainerTest { }); // Wait for RPC call from the server and send the response - MqttEvent requestFromServer = listener.getEvents().poll(10, TimeUnit.SECONDS); + MqttEvent requestFromServer = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS); Assert.assertEquals("{\"method\":\"getValue\",\"params\":true}", Objects.requireNonNull(requestFromServer).getMessage()); @@ -287,7 +284,7 @@ public class MqttClientTest extends AbstractContainerTest { // Send a response to the server's RPC request mqttClient.publish("v1/devices/me/rpc/response/" + requestId, Unpooled.wrappedBuffer(clientResponse.toString().getBytes())).get(); - ResponseEntity serverResponse = future.get(5, TimeUnit.SECONDS); + ResponseEntity serverResponse = future.get(5 * timeoutMultiplier, TimeUnit.SECONDS); service.shutdownNow(); Assert.assertTrue(serverResponse.getStatusCode().is2xxSuccessful()); Assert.assertEquals(clientResponse.toString(), serverResponse.getBody()); @@ -311,7 +308,7 @@ public class MqttClientTest extends AbstractContainerTest { // Create a new root rule chain RuleChainId ruleChainId = createRootRuleChainForRpcResponse(); - TimeUnit.SECONDS.sleep(3); + TimeUnit.SECONDS.sleep(3 * timeoutMultiplier); // Send the request to the server JsonObject clientRequest = new JsonObject(); clientRequest.addProperty("method", "getResponse"); @@ -320,8 +317,8 @@ public class MqttClientTest extends AbstractContainerTest { mqttClient.publish("v1/devices/me/rpc/request/" + requestId, Unpooled.wrappedBuffer(clientRequest.toString().getBytes())).get(); // Check the response from the server - TimeUnit.SECONDS.sleep(1); - MqttEvent responseFromServer = listener.getEvents().poll(1, TimeUnit.SECONDS); + TimeUnit.SECONDS.sleep(1 * timeoutMultiplier); + MqttEvent responseFromServer = listener.getEvents().poll(1 * timeoutMultiplier, TimeUnit.SECONDS); Integer responseId = Integer.valueOf(Objects.requireNonNull(responseFromServer).getTopic().substring("v1/devices/me/rpc/response/".length())); Assert.assertEquals(requestId, responseId); Assert.assertEquals("requestReceived", mapper.readTree(responseFromServer.getMessage()).get("response").asText()); @@ -350,7 +347,7 @@ public class MqttClientTest extends AbstractContainerTest { MqttClient mqttClient = getMqttClient(deviceCredentials, listener); restClient.deleteDevice(device.getId()); - TimeUnit.SECONDS.sleep(3); + TimeUnit.SECONDS.sleep(3 * timeoutMultiplier); Assert.assertFalse(mqttClient.isConnected()); } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttGatewayClientTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttGatewayClientTest.java index ec77cbec36..46aa4acd1d 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttGatewayClientTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttGatewayClientTest.java @@ -168,7 +168,7 @@ public class MqttGatewayClientTest extends AbstractContainerTest { mapper.readTree(sharedAttributes.toString()), ResponseEntity.class, createdDevice.getId()); Assert.assertTrue(sharedAttributesResponse.getStatusCode().is2xxSuccessful()); - var event = listener.getEvents().poll(10, TimeUnit.SECONDS); + var event = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS); JsonObject requestData = new JsonObject(); requestData.addProperty("id", 1); @@ -178,7 +178,7 @@ public class MqttGatewayClientTest extends AbstractContainerTest { mqttClient.on("v1/gateway/attributes/response", listener, MqttQoS.AT_LEAST_ONCE).get(); mqttClient.publish("v1/gateway/attributes/request", Unpooled.wrappedBuffer(requestData.toString().getBytes())).get(); - event = listener.getEvents().poll(10, TimeUnit.SECONDS); + event = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS); JsonObject responseData = jsonParser.parse(Objects.requireNonNull(event).getMessage()).getAsJsonObject(); Assert.assertTrue(responseData.has("value")); @@ -195,7 +195,7 @@ public class MqttGatewayClientTest extends AbstractContainerTest { mqttClient.on("v1/gateway/attributes/response", listener, MqttQoS.AT_LEAST_ONCE).get(); mqttClient.publish("v1/gateway/attributes/request", Unpooled.wrappedBuffer(requestData.toString().getBytes())).get(); - event = listener.getEvents().poll(10, TimeUnit.SECONDS); + event = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS); responseData = jsonParser.parse(Objects.requireNonNull(event).getMessage()).getAsJsonObject(); Assert.assertTrue(responseData.has("values")); @@ -213,7 +213,7 @@ public class MqttGatewayClientTest extends AbstractContainerTest { mqttClient.on("v1/gateway/attributes/response", listener, MqttQoS.AT_LEAST_ONCE).get(); mqttClient.publish("v1/gateway/attributes/request", Unpooled.wrappedBuffer(requestData.toString().getBytes())).get(); - event = listener.getEvents().poll(10, TimeUnit.SECONDS); + event = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS); responseData = jsonParser.parse(Objects.requireNonNull(event).getMessage()).getAsJsonObject(); Assert.assertTrue(responseData.has("values")); @@ -256,7 +256,7 @@ public class MqttGatewayClientTest extends AbstractContainerTest { mapper.readTree(sharedAttributes.toString()), ResponseEntity.class, createdDevice.getId()); Assert.assertTrue(sharedAttributesResponse.getStatusCode().is2xxSuccessful()); - MqttEvent sharedAttributeEvent = listener.getEvents().poll(10, TimeUnit.SECONDS); + MqttEvent sharedAttributeEvent = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS); // Catch attribute update event Assert.assertNotNull(sharedAttributeEvent); @@ -266,7 +266,7 @@ public class MqttGatewayClientTest extends AbstractContainerTest { mqttClient.on("v1/gateway/attributes/response", listener, MqttQoS.AT_LEAST_ONCE).get(); // Wait until subscription is processed - TimeUnit.SECONDS.sleep(3); + TimeUnit.SECONDS.sleep(3 * timeoutMultiplier); checkAttribute(true, clientAttributeValue); checkAttribute(false, sharedAttributeValue); @@ -276,7 +276,7 @@ public class MqttGatewayClientTest extends AbstractContainerTest { public void subscribeToAttributeUpdatesFromServer() throws Exception { mqttClient.on("v1/gateway/attributes", listener, MqttQoS.AT_LEAST_ONCE).get(); // Wait until subscription is processed - TimeUnit.SECONDS.sleep(3); + TimeUnit.SECONDS.sleep(3 * timeoutMultiplier); String sharedAttributeName = "sharedAttr"; // Add a new shared attribute @@ -294,7 +294,7 @@ public class MqttGatewayClientTest extends AbstractContainerTest { createdDevice.getId()); Assert.assertTrue(sharedAttributesResponse.getStatusCode().is2xxSuccessful()); - MqttEvent event = listener.getEvents().poll(10, TimeUnit.SECONDS); + MqttEvent event = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS); Assert.assertEquals(sharedAttributeValue, mapper.readValue(Objects.requireNonNull(event).getMessage(), JsonNode.class).get("data").get(sharedAttributeName).asText()); @@ -313,7 +313,7 @@ public class MqttGatewayClientTest extends AbstractContainerTest { createdDevice.getId()); Assert.assertTrue(updatedSharedAttributesResponse.getStatusCode().is2xxSuccessful()); - event = listener.getEvents().poll(10, TimeUnit.SECONDS); + event = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS); Assert.assertEquals(updatedSharedAttributeValue, mapper.readValue(Objects.requireNonNull(event).getMessage(), JsonNode.class).get("data").get(sharedAttributeName).asText()); } @@ -324,7 +324,7 @@ public class MqttGatewayClientTest extends AbstractContainerTest { mqttClient.on(gatewayRpcTopic, listener, MqttQoS.AT_LEAST_ONCE).get(); // Wait until subscription is processed - TimeUnit.SECONDS.sleep(3); + TimeUnit.SECONDS.sleep(3 * timeoutMultiplier); // Send an RPC from the server JsonObject serverRpcPayload = new JsonObject(); @@ -343,7 +343,7 @@ public class MqttGatewayClientTest extends AbstractContainerTest { }); // Wait for RPC call from the server and send the response - MqttEvent requestFromServer = listener.getEvents().poll(10, TimeUnit.SECONDS); + MqttEvent requestFromServer = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS); service.shutdownNow(); Assert.assertNotNull(requestFromServer); @@ -369,7 +369,7 @@ public class MqttGatewayClientTest extends AbstractContainerTest { // Send a response to the server's RPC request mqttClient.publish(gatewayRpcTopic, Unpooled.wrappedBuffer(gatewayResponse.toString().getBytes())).get(); - ResponseEntity serverResponse = future.get(5, TimeUnit.SECONDS); + ResponseEntity serverResponse = future.get(5 * timeoutMultiplier, TimeUnit.SECONDS); Assert.assertTrue(serverResponse.getStatusCode().is2xxSuccessful()); Assert.assertEquals(clientResponse.toString(), serverResponse.getBody()); } @@ -396,7 +396,7 @@ public class MqttGatewayClientTest extends AbstractContainerTest { gatewayAttributesRequest.addProperty("key", attributeName); log.info(gatewayAttributesRequest.toString()); mqttClient.publish("v1/gateway/attributes/request", Unpooled.wrappedBuffer(gatewayAttributesRequest.toString().getBytes())).get(); - MqttEvent clientAttributeEvent = listener.getEvents().poll(10, TimeUnit.SECONDS); + MqttEvent clientAttributeEvent = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS); Assert.assertNotNull(clientAttributeEvent); JsonObject responseMessage = new JsonParser().parse(Objects.requireNonNull(clientAttributeEvent).getMessage()).getAsJsonObject(); @@ -407,9 +407,17 @@ public class MqttGatewayClientTest extends AbstractContainerTest { } private Device createDeviceThroughGateway(MqttClient mqttClient, Device gatewayDevice) throws Exception { + if (timeoutMultiplier > 1) { + TimeUnit.SECONDS.sleep(30); + } + String deviceName = "mqtt_device"; mqttClient.publish("v1/gateway/connect", Unpooled.wrappedBuffer(createGatewayConnectPayload(deviceName).toString().getBytes()), MqttQoS.AT_LEAST_ONCE).get(); + if (timeoutMultiplier > 1) { + TimeUnit.SECONDS.sleep(30); + } + List relations = restClient.findByFrom(gatewayDevice.getId(), RelationTypeGroup.COMMON); Assert.assertEquals(1, relations.size()); diff --git a/msa/black-box-tests/src/test/resources/docker-compose.rabbitmq-server.yml b/msa/black-box-tests/src/test/resources/docker-compose.rabbitmq-server.yml new file mode 100644 index 0000000000..21aba7061a --- /dev/null +++ b/msa/black-box-tests/src/test/resources/docker-compose.rabbitmq-server.yml @@ -0,0 +1,69 @@ +# +# Copyright © 2016-2022 The Thingsboard Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +version: '2.2' + +services: + rabbitmq: + restart: always + image: rabbitmq:3 + ports: + - '5672:5672' + environment: + RABBITMQ_DEFAULT_USER: YOUR_USERNAME + RABBITMQ_DEFAULT_PASS: YOUR_PASSWORD + tb-js-executor: + depends_on: + - rabbitmq + tb-core1: + depends_on: + - rabbitmq + tb-core2: + depends_on: + - rabbitmq + tb-rule-engine1: + depends_on: + - rabbitmq + tb-rule-engine2: + depends_on: + - rabbitmq + tb-mqtt-transport1: + depends_on: + - rabbitmq + tb-mqtt-transport2: + depends_on: + - rabbitmq + tb-http-transport1: + depends_on: + - rabbitmq + tb-http-transport2: + depends_on: + - rabbitmq + tb-coap-transport: + depends_on: + - rabbitmq + tb-lwm2m-transport: + depends_on: + - rabbitmq + tb-snmp-transport: + depends_on: + - rabbitmq + tb-vc-executor1: + depends_on: + - rabbitmq + tb-vc-executor2: + depends_on: + - rabbitmq diff --git a/msa/js-executor/api/httpServer.ts b/msa/js-executor/api/httpServer.ts new file mode 100644 index 0000000000..e1c294fdff --- /dev/null +++ b/msa/js-executor/api/httpServer.ts @@ -0,0 +1,65 @@ +/// +/// Copyright © 2016-2022 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 express from 'express'; +import { _logger} from '../config/logger'; +import http from 'http'; +import { Socket } from 'net'; + +export class HttpServer { + + private logger = _logger('httpServer'); + private app = express(); + private server: http.Server | null; + private connections: Socket[] = []; + + constructor(httpPort: number) { + this.app.get('/livenessProbe', async (req, res) => { + const message = { + now: new Date().toISOString() + }; + res.send(message); + }) + + this.server = this.app.listen(httpPort, () => { + this.logger.info('Started HTTP endpoint on port %s. Please, use /livenessProbe !', httpPort); + }).on('error', (error) => { + this.logger.error(error); + }); + + this.server.on('connection', connection => { + this.connections.push(connection); + connection.on('close', () => this.connections = this.connections.filter(curr => curr !== connection)); + }); + } + + async stop() { + if (this.server) { + this.logger.info('Stopping HTTP Server...'); + const _server = this.server; + this.server = null; + this.connections.forEach(curr => curr.end(() => curr.destroy())); + await new Promise( + (resolve, reject) => { + _server.close((err) => { + this.logger.info('HTTP Server stopped.'); + resolve(); + }); + } + ); + } + } +} diff --git a/msa/js-executor/api/jsExecutor.js b/msa/js-executor/api/jsExecutor.js deleted file mode 100644 index 02190406d2..0000000000 --- a/msa/js-executor/api/jsExecutor.js +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright © 2016-2022 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -'use strict'; - -const vm = require('vm'); - -function JsExecutor(useSandbox) { - this.useSandbox = useSandbox; -} - -JsExecutor.prototype.compileScript = function(code) { - if (this.useSandbox) { - return createScript(code); - } else { - return createFunction(code); - } -} - -JsExecutor.prototype.executeScript = function(script, args, timeout) { - if (this.useSandbox) { - return invokeScript(script, args, timeout); - } else { - return invokeFunction(script, args); - } -} - -function createScript(code) { - return new Promise((resolve, reject) => { - try { - code = "("+code+")(...args)"; - var script = new vm.Script(code); - resolve(script); - } catch (err) { - reject(err); - } - }); -} - -function invokeScript(script, args, timeout) { - return new Promise((resolve, reject) => { - try { - var sandbox = Object.create(null); - sandbox.args = args; - var result = script.runInNewContext(sandbox, {timeout: timeout}); - resolve(result); - } catch (err) { - reject(err); - } - }); -} - - -function createFunction(code) { - return new Promise((resolve, reject) => { - try { - code = "return ("+code+")(...args)"; - const parsingContext = vm.createContext({}); - const func = vm.compileFunction(code, ['args'], {parsingContext: parsingContext}); - resolve(func); - } catch (err) { - reject(err); - } - }); -} - -function invokeFunction(func, args) { - return new Promise((resolve, reject) => { - try { - var result = func(args); - resolve(result); - } catch (err) { - reject(err); - } - }); -} - -module.exports = JsExecutor; diff --git a/msa/js-executor/api/jsExecutor.models.ts b/msa/js-executor/api/jsExecutor.models.ts new file mode 100644 index 0000000000..db2ced52c4 --- /dev/null +++ b/msa/js-executor/api/jsExecutor.models.ts @@ -0,0 +1,69 @@ +/// +/// Copyright © 2016-2022 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 TbMessage { + scriptIdMSB: string; + scriptIdLSB: string; +} + +export interface RemoteJsRequest { + compileRequest?: JsCompileRequest; + invokeRequest?: JsInvokeRequest; + releaseRequest?: JsReleaseRequest; +} + +export interface JsReleaseRequest extends TbMessage { + functionName: string; +} + +export interface JsInvokeRequest extends TbMessage { + functionName: string; + scriptBody: string; + timeout: number; + args: string[]; +} + +export interface JsCompileRequest extends TbMessage { + functionName: string; + scriptBody: string; +} + + +export interface JsReleaseResponse extends TbMessage { + success: boolean; +} + +export interface JsCompileResponse extends TbMessage { + success: boolean; + errorCode?: number; + errorDetails?: string; +} + +export interface JsInvokeResponse { + success: boolean; + result: string; + errorCode?: number; + errorDetails?: string; +} + +export interface RemoteJsResponse { + requestIdMSB: string; + requestIdLSB: string; + compileResponse?: JsCompileResponse; + invokeResponse?: JsInvokeResponse; + releaseResponse?: JsReleaseResponse; +} diff --git a/msa/js-executor/api/jsExecutor.ts b/msa/js-executor/api/jsExecutor.ts new file mode 100644 index 0000000000..f22f14281b --- /dev/null +++ b/msa/js-executor/api/jsExecutor.ts @@ -0,0 +1,93 @@ +/// +/// Copyright © 2016-2022 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT 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 vm, { Script } from 'vm'; + +export type TbScript = Script | Function; + +export class JsExecutor { + useSandbox: boolean; + + constructor(useSandbox: boolean) { + this.useSandbox = useSandbox; + } + + compileScript(code: string): Promise { + if (this.useSandbox) { + return this.createScript(code); + } else { + return this.createFunction(code); + } + } + + executeScript(script: TbScript, args: string[], timeout?: number): Promise { + if (this.useSandbox) { + return this.invokeScript(script as Script, args, timeout); + } else { + return this.invokeFunction(script as Function, args); + } + } + + private createScript(code: string): Promise