336 changed files with 11227 additions and 6481 deletions
@ -0,0 +1,71 @@ |
|||
-- |
|||
-- Copyright © 2016-2021 The Thingsboard Authors |
|||
-- |
|||
-- Licensed under the Apache License, Version 2.0 (the "License"); |
|||
-- you may not use this file except in compliance with the License. |
|||
-- You may obtain a copy of the License at |
|||
-- |
|||
-- http://www.apache.org/licenses/LICENSE-2.0 |
|||
-- |
|||
-- Unless required by applicable law or agreed to in writing, software |
|||
-- distributed under the License is distributed on an "AS IS" BASIS, |
|||
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
-- See the License for the specific language governing permissions and |
|||
-- limitations under the License. |
|||
-- |
|||
|
|||
CREATE TABLE IF NOT EXISTS entity_alarm ( |
|||
tenant_id uuid NOT NULL, |
|||
entity_type varchar(32), |
|||
entity_id uuid NOT NULL, |
|||
created_time bigint NOT NULL, |
|||
alarm_type varchar(255) NOT NULL, |
|||
customer_id uuid, |
|||
alarm_id uuid, |
|||
CONSTRAINT entity_alarm_pkey PRIMARY KEY (entity_id, alarm_id), |
|||
CONSTRAINT fk_entity_alarm_id FOREIGN KEY (alarm_id) REFERENCES alarm(id) ON DELETE CASCADE |
|||
); |
|||
|
|||
CREATE INDEX IF NOT EXISTS idx_alarm_tenant_status_created_time ON alarm(tenant_id, status, created_time DESC); |
|||
CREATE INDEX IF NOT EXISTS idx_entity_alarm_created_time ON entity_alarm(tenant_id, entity_id, created_time DESC); |
|||
CREATE INDEX IF NOT EXISTS idx_entity_alarm_alarm_id ON entity_alarm(alarm_id); |
|||
|
|||
INSERT INTO entity_alarm(tenant_id, entity_type, entity_id, created_time, alarm_type, customer_id, alarm_id) |
|||
SELECT tenant_id, |
|||
CASE |
|||
WHEN originator_type = 0 THEN 'TENANT' |
|||
WHEN originator_type = 1 THEN 'CUSTOMER' |
|||
WHEN originator_type = 2 THEN 'USER' |
|||
WHEN originator_type = 3 THEN 'DASHBOARD' |
|||
WHEN originator_type = 4 THEN 'ASSET' |
|||
WHEN originator_type = 5 THEN 'DEVICE' |
|||
WHEN originator_type = 6 THEN 'ALARM' |
|||
WHEN originator_type = 7 THEN 'RULE_CHAIN' |
|||
WHEN originator_type = 8 THEN 'RULE_NODE' |
|||
WHEN originator_type = 9 THEN 'ENTITY_VIEW' |
|||
WHEN originator_type = 10 THEN 'WIDGETS_BUNDLE' |
|||
WHEN originator_type = 11 THEN 'WIDGET_TYPE' |
|||
WHEN originator_type = 12 THEN 'TENANT_PROFILE' |
|||
WHEN originator_type = 13 THEN 'DEVICE_PROFILE' |
|||
WHEN originator_type = 14 THEN 'API_USAGE_STATE' |
|||
WHEN originator_type = 15 THEN 'TB_RESOURCE' |
|||
WHEN originator_type = 16 THEN 'OTA_PACKAGE' |
|||
WHEN originator_type = 17 THEN 'EDGE' |
|||
WHEN originator_type = 18 THEN 'RPC' |
|||
else 'UNKNOWN' |
|||
END, |
|||
originator_id, |
|||
created_time, |
|||
type, |
|||
customer_id, |
|||
id |
|||
FROM alarm |
|||
ON CONFLICT DO NOTHING; |
|||
|
|||
INSERT INTO entity_alarm(tenant_id, entity_type, entity_id, created_time, alarm_type, customer_id, alarm_id) |
|||
SELECT a.tenant_id, r.from_type, r.from_id, created_time, type, customer_id, id |
|||
FROM alarm a |
|||
INNER JOIN relation r ON r.relation_type_group = 'ALARM' and r.relation_type = 'ANY' and a.id = r.to_id |
|||
ON CONFLICT DO NOTHING; |
|||
|
|||
DELETE FROM relation r WHERE r.relation_type_group = 'ALARM'; |
|||
@ -0,0 +1,41 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT 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.actors.ruleChain; |
|||
|
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.Getter; |
|||
import lombok.ToString; |
|||
import org.thingsboard.server.common.data.id.RuleChainId; |
|||
import org.thingsboard.server.common.data.id.RuleNodeId; |
|||
import org.thingsboard.server.common.msg.MsgType; |
|||
import org.thingsboard.server.common.msg.TbMsg; |
|||
|
|||
/** |
|||
* Created by ashvayka on 19.03.18. |
|||
*/ |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString |
|||
public final class RuleChainInputMsg extends TbToRuleChainActorMsg { |
|||
|
|||
public RuleChainInputMsg(RuleChainId target, TbMsg tbMsg) { |
|||
super(tbMsg, target); |
|||
} |
|||
|
|||
@Override |
|||
public MsgType getMsgType() { |
|||
return MsgType.RULE_CHAIN_INPUT_MSG; |
|||
} |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT 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.actors.ruleChain; |
|||
|
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.Getter; |
|||
import lombok.ToString; |
|||
import org.thingsboard.server.common.data.id.RuleChainId; |
|||
import org.thingsboard.server.common.data.id.RuleNodeId; |
|||
import org.thingsboard.server.common.msg.MsgType; |
|||
import org.thingsboard.server.common.msg.TbMsg; |
|||
|
|||
/** |
|||
* Created by ashvayka on 19.03.18. |
|||
*/ |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString |
|||
public final class RuleChainOutputMsg extends TbToRuleChainActorMsg { |
|||
|
|||
@Getter |
|||
private final RuleNodeId targetRuleNodeId; |
|||
|
|||
@Getter |
|||
private final String relationType; |
|||
|
|||
public RuleChainOutputMsg(RuleChainId target, RuleNodeId targetRuleNodeId, String relationType, TbMsg tbMsg) { |
|||
super(tbMsg, target); |
|||
this.targetRuleNodeId = targetRuleNodeId; |
|||
this.relationType = relationType; |
|||
} |
|||
|
|||
@Override |
|||
public MsgType getMsgType() { |
|||
return MsgType.RULE_CHAIN_OUTPUT_MSG; |
|||
} |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT 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.actors.ruleChain; |
|||
|
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.Getter; |
|||
import lombok.ToString; |
|||
import org.thingsboard.server.common.data.id.RuleChainId; |
|||
import org.thingsboard.server.common.msg.TbActorStopReason; |
|||
import org.thingsboard.server.common.msg.TbMsg; |
|||
import org.thingsboard.server.common.msg.TbRuleEngineActorMsg; |
|||
import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg; |
|||
import org.thingsboard.server.common.msg.queue.RuleEngineException; |
|||
|
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString |
|||
public abstract class TbToRuleChainActorMsg extends TbRuleEngineActorMsg implements RuleChainAwareMsg { |
|||
|
|||
@Getter |
|||
private final RuleChainId target; |
|||
|
|||
public TbToRuleChainActorMsg(TbMsg msg, RuleChainId target) { |
|||
super(msg); |
|||
this.target = target; |
|||
} |
|||
|
|||
@Override |
|||
public RuleChainId getRuleChainId() { |
|||
return target; |
|||
} |
|||
|
|||
@Override |
|||
public void onTbActorStopped(TbActorStopReason reason) { |
|||
String message = reason == TbActorStopReason.STOPPED ? String.format("Rule chain [%s] stopped", target.getId()) : String.format("Failed to initialize rule chain [%s]!", target.getId()); |
|||
msg.getCallback().onFailure(new RuleEngineException(message)); |
|||
} |
|||
} |
|||
@ -0,0 +1,191 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT 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.rule; |
|||
|
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.common.util.JacksonUtil; |
|||
import org.thingsboard.rule.engine.flow.TbRuleChainInputNode; |
|||
import org.thingsboard.rule.engine.flow.TbRuleChainInputNodeConfiguration; |
|||
import org.thingsboard.rule.engine.flow.TbRuleChainOutputNode; |
|||
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.relation.EntityRelation; |
|||
import org.thingsboard.server.common.data.rule.RuleChain; |
|||
import org.thingsboard.server.common.data.rule.RuleChainMetaData; |
|||
import org.thingsboard.server.common.data.rule.RuleChainOutputLabelsUsage; |
|||
import org.thingsboard.server.common.data.rule.RuleChainUpdateResult; |
|||
import org.thingsboard.server.common.data.rule.RuleNode; |
|||
import org.thingsboard.server.common.data.rule.RuleNodeUpdateResult; |
|||
import org.thingsboard.server.dao.relation.RelationService; |
|||
import org.thingsboard.server.dao.rule.RuleChainService; |
|||
import org.thingsboard.server.queue.util.TbCoreComponent; |
|||
|
|||
import java.util.Collections; |
|||
import java.util.Comparator; |
|||
import java.util.HashMap; |
|||
import java.util.HashSet; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.Set; |
|||
import java.util.TreeSet; |
|||
import java.util.stream.Collectors; |
|||
|
|||
@RequiredArgsConstructor |
|||
@Service |
|||
@TbCoreComponent |
|||
@Slf4j |
|||
public class DefaultTbRuleChainService implements TbRuleChainService { |
|||
|
|||
private final RuleChainService ruleChainService; |
|||
private final RelationService relationService; |
|||
|
|||
@Override |
|||
public Set<String> getRuleChainOutputLabels(TenantId tenantId, RuleChainId ruleChainId) { |
|||
RuleChainMetaData metaData = ruleChainService.loadRuleChainMetaData(tenantId, ruleChainId); |
|||
Set<String> outputLabels = new TreeSet<>(); |
|||
for (RuleNode ruleNode : metaData.getNodes()) { |
|||
if (isOutputRuleNode(ruleNode)) { |
|||
outputLabels.add(ruleNode.getName()); |
|||
} |
|||
} |
|||
return outputLabels; |
|||
} |
|||
|
|||
@Override |
|||
public List<RuleChainOutputLabelsUsage> getOutputLabelUsage(TenantId tenantId, RuleChainId ruleChainId) { |
|||
List<RuleNode> ruleNodes = ruleChainService.findRuleNodesByTenantIdAndType(tenantId, TbRuleChainInputNode.class.getName(), ruleChainId.getId().toString()); |
|||
Map<RuleChainId, String> ruleChainNamesCache = new HashMap<>(); |
|||
// Additional filter, "just in case" the structure of the JSON configuration will change.
|
|||
var filteredRuleNodes = ruleNodes.stream().filter(node -> { |
|||
try { |
|||
TbRuleChainInputNodeConfiguration configuration = JacksonUtil.treeToValue(node.getConfiguration(), TbRuleChainInputNodeConfiguration.class); |
|||
return ruleChainId.getId().toString().equals(configuration.getRuleChainId()); |
|||
} catch (Exception e) { |
|||
log.warn("[{}][{}] Failed to decode rule node configuration", tenantId, ruleChainId, e); |
|||
return false; |
|||
} |
|||
}).collect(Collectors.toList()); |
|||
|
|||
|
|||
return filteredRuleNodes.stream() |
|||
.map(ruleNode -> { |
|||
RuleChainOutputLabelsUsage usage = new RuleChainOutputLabelsUsage(); |
|||
usage.setRuleNodeId(ruleNode.getId()); |
|||
usage.setRuleNodeName(ruleNode.getName()); |
|||
usage.setRuleChainId(ruleNode.getRuleChainId()); |
|||
List<EntityRelation> relations = ruleChainService.getRuleNodeRelations(tenantId, ruleNode.getId()); |
|||
if (relations != null && !relations.isEmpty()) { |
|||
usage.setLabels(relations.stream().map(EntityRelation::getType).collect(Collectors.toSet())); |
|||
} |
|||
return usage; |
|||
}) |
|||
.filter(usage -> usage.getLabels() != null) |
|||
.peek(usage -> { |
|||
String ruleChainName = ruleChainNamesCache.computeIfAbsent(usage.getRuleChainId(), |
|||
id -> ruleChainService.findRuleChainById(tenantId, id).getName()); |
|||
usage.setRuleChainName(ruleChainName); |
|||
}) |
|||
.sorted(Comparator |
|||
.comparing(RuleChainOutputLabelsUsage::getRuleChainName) |
|||
.thenComparing(RuleChainOutputLabelsUsage::getRuleNodeName)) |
|||
.collect(Collectors.toList()); |
|||
} |
|||
|
|||
@Override |
|||
public List<RuleChain> updateRelatedRuleChains(TenantId tenantId, RuleChainId ruleChainId, RuleChainUpdateResult result) { |
|||
Set<RuleChainId> ruleChainIds = new HashSet<>(); |
|||
log.debug("[{}][{}] Going to update links in related rule chains", tenantId, ruleChainId); |
|||
if (result.getUpdatedRuleNodes() == null || result.getUpdatedRuleNodes().isEmpty()) { |
|||
return Collections.emptyList(); |
|||
} |
|||
|
|||
Set<String> oldLabels = new HashSet<>(); |
|||
Set<String> newLabels = new HashSet<>(); |
|||
Set<String> confusedLabels = new HashSet<>(); |
|||
Map<String, String> updatedLabels = new HashMap<>(); |
|||
for (RuleNodeUpdateResult update : result.getUpdatedRuleNodes()) { |
|||
var oldNode = update.getOldRuleNode(); |
|||
var newNode = update.getNewRuleNode(); |
|||
if (isOutputRuleNode(newNode)) { |
|||
try { |
|||
oldLabels.add(oldNode.getName()); |
|||
newLabels.add(newNode.getName()); |
|||
if (!oldNode.getName().equals(newNode.getName())) { |
|||
String oldLabel = oldNode.getName(); |
|||
String newLabel = newNode.getName(); |
|||
if (updatedLabels.containsKey(oldLabel) && !updatedLabels.get(oldLabel).equals(newLabel)) { |
|||
confusedLabels.add(oldLabel); |
|||
log.warn("[{}][{}] Can't automatically rename the label from [{}] to [{}] due to conflict [{}]", tenantId, ruleChainId, oldLabel, newLabel, updatedLabels.get(oldLabel)); |
|||
} else { |
|||
updatedLabels.put(oldLabel, newLabel); |
|||
} |
|||
|
|||
} |
|||
} catch (Exception e) { |
|||
log.warn("[{}][{}][{}] Failed to decode rule node configuration", tenantId, ruleChainId, newNode.getId(), e); |
|||
} |
|||
} |
|||
} |
|||
// Remove all output labels that are renamed to two or more different labels, since we don't which new label to use;
|
|||
confusedLabels.forEach(updatedLabels::remove); |
|||
// Remove all output labels that are renamed but still present in the rule chain;
|
|||
newLabels.forEach(updatedLabels::remove); |
|||
if (!oldLabels.equals(newLabels)) { |
|||
ruleChainIds.addAll(updateRelatedRuleChains(tenantId, ruleChainId, updatedLabels)); |
|||
} |
|||
return ruleChainIds.stream().map(id -> ruleChainService.findRuleChainById(tenantId, id)).collect(Collectors.toList()); |
|||
} |
|||
|
|||
public Set<RuleChainId> updateRelatedRuleChains(TenantId tenantId, RuleChainId ruleChainId, Map<String, String> labelsMap) { |
|||
Set<RuleChainId> updatedRuleChains = new HashSet<>(); |
|||
List<RuleChainOutputLabelsUsage> usageList = getOutputLabelUsage(tenantId, ruleChainId); |
|||
for (RuleChainOutputLabelsUsage usage : usageList) { |
|||
labelsMap.forEach((oldLabel, newLabel) -> { |
|||
if (usage.getLabels().contains(oldLabel)) { |
|||
updatedRuleChains.add(usage.getRuleChainId()); |
|||
renameOutgoingLinks(tenantId, usage.getRuleNodeId(), oldLabel, newLabel); |
|||
} |
|||
}); |
|||
} |
|||
return updatedRuleChains; |
|||
} |
|||
|
|||
private void renameOutgoingLinks(TenantId tenantId, RuleNodeId ruleNodeId, String oldLabel, String newLabel) { |
|||
List<EntityRelation> relations = ruleChainService.getRuleNodeRelations(tenantId, ruleNodeId); |
|||
for (EntityRelation relation : relations) { |
|||
if (relation.getType().equals(oldLabel)) { |
|||
relationService.deleteRelation(tenantId, relation); |
|||
relation.setType(newLabel); |
|||
relationService.saveRelation(tenantId, relation); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private boolean isOutputRuleNode(RuleNode ruleNode) { |
|||
return isRuleNode(ruleNode, TbRuleChainOutputNode.class); |
|||
} |
|||
|
|||
private boolean isInputRuleNode(RuleNode ruleNode) { |
|||
return isRuleNode(ruleNode, TbRuleChainInputNode.class); |
|||
} |
|||
|
|||
private boolean isRuleNode(RuleNode ruleNode, Class<?> clazz) { |
|||
return ruleNode != null && ruleNode.getType().equals(clazz.getName()); |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT 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.rule; |
|||
|
|||
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.RuleChainOutputLabelsUsage; |
|||
import org.thingsboard.server.common.data.rule.RuleChainUpdateResult; |
|||
|
|||
import java.util.List; |
|||
import java.util.Set; |
|||
|
|||
public interface TbRuleChainService { |
|||
|
|||
Set<String> getRuleChainOutputLabels(TenantId tenantId, RuleChainId ruleChainId); |
|||
|
|||
List<RuleChainOutputLabelsUsage> getOutputLabelUsage(TenantId tenantId, RuleChainId ruleChainId); |
|||
|
|||
List<RuleChain> updateRelatedRuleChains(TenantId tenantId, RuleChainId ruleChainId, RuleChainUpdateResult result); |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT 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.transport; |
|||
|
|||
enum BasicCredentialsValidationResult {HASH_MISMATCH, PASSWORD_MISMATCH, VALID} |
|||
@ -0,0 +1,257 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT 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.actors; |
|||
|
|||
import org.junit.jupiter.api.Test; |
|||
import org.junit.jupiter.api.extension.ExtendWith; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.boot.context.properties.EnableConfigurationProperties; |
|||
import org.springframework.boot.test.mock.mockito.MockBean; |
|||
import org.springframework.data.redis.core.RedisTemplate; |
|||
import org.springframework.test.context.ContextConfiguration; |
|||
import org.springframework.test.context.TestPropertySource; |
|||
import org.springframework.test.context.junit.jupiter.SpringExtension; |
|||
import org.thingsboard.rule.engine.api.MailService; |
|||
import org.thingsboard.rule.engine.api.SmsService; |
|||
import org.thingsboard.rule.engine.api.sms.SmsSenderFactory; |
|||
import org.thingsboard.server.actors.service.ActorService; |
|||
import org.thingsboard.server.cluster.TbClusterService; |
|||
import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; |
|||
import org.thingsboard.server.dao.asset.AssetService; |
|||
import org.thingsboard.server.dao.attributes.AttributesService; |
|||
import org.thingsboard.server.dao.audit.AuditLogService; |
|||
import org.thingsboard.server.dao.cassandra.CassandraCluster; |
|||
import org.thingsboard.server.dao.customer.CustomerService; |
|||
import org.thingsboard.server.dao.dashboard.DashboardService; |
|||
import org.thingsboard.server.dao.device.ClaimDevicesService; |
|||
import org.thingsboard.server.dao.device.DeviceService; |
|||
import org.thingsboard.server.dao.edge.EdgeEventService; |
|||
import org.thingsboard.server.dao.edge.EdgeService; |
|||
import org.thingsboard.server.dao.entityview.EntityViewService; |
|||
import org.thingsboard.server.dao.event.EventService; |
|||
import org.thingsboard.server.dao.nosql.CassandraBufferedRateReadExecutor; |
|||
import org.thingsboard.server.dao.nosql.CassandraBufferedRateWriteExecutor; |
|||
import org.thingsboard.server.dao.ota.OtaPackageService; |
|||
import org.thingsboard.server.dao.relation.RelationService; |
|||
import org.thingsboard.server.dao.resource.ResourceService; |
|||
import org.thingsboard.server.dao.rule.RuleChainService; |
|||
import org.thingsboard.server.dao.rule.RuleNodeStateService; |
|||
import org.thingsboard.server.dao.tenant.TbTenantProfileCache; |
|||
import org.thingsboard.server.dao.tenant.TenantProfileService; |
|||
import org.thingsboard.server.dao.tenant.TenantService; |
|||
import org.thingsboard.server.dao.timeseries.TimeseriesService; |
|||
import org.thingsboard.server.dao.user.UserService; |
|||
import org.thingsboard.server.queue.discovery.PartitionService; |
|||
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; |
|||
import org.thingsboard.server.queue.usagestats.TbApiUsageClient; |
|||
import org.thingsboard.server.service.apiusage.TbApiUsageStateService; |
|||
import org.thingsboard.server.service.component.ComponentDiscoveryService; |
|||
import org.thingsboard.server.service.edge.rpc.EdgeRpcService; |
|||
import org.thingsboard.server.service.executors.DbCallbackExecutorService; |
|||
import org.thingsboard.server.service.executors.ExternalCallExecutorService; |
|||
import org.thingsboard.server.service.executors.SharedEventLoopGroupService; |
|||
import org.thingsboard.server.service.mail.MailExecutorService; |
|||
import org.thingsboard.server.service.profile.TbDeviceProfileCache; |
|||
import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService; |
|||
import org.thingsboard.server.service.rpc.TbRpcService; |
|||
import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService; |
|||
import org.thingsboard.server.service.script.JsInvokeService; |
|||
import org.thingsboard.server.service.session.DeviceSessionCacheService; |
|||
import org.thingsboard.server.service.sms.SmsExecutorService; |
|||
import org.thingsboard.server.service.state.DeviceStateService; |
|||
import org.thingsboard.server.service.telemetry.AlarmSubscriptionService; |
|||
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; |
|||
import org.thingsboard.server.service.transport.TbCoreToTransportService; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThat; |
|||
|
|||
@ExtendWith(SpringExtension.class) |
|||
@ContextConfiguration(classes = ActorSystemContext.class) |
|||
@EnableConfigurationProperties |
|||
@TestPropertySource(properties = { |
|||
"cache.type=caffeine", |
|||
}) |
|||
public class ActorSystemContextTest { |
|||
|
|||
@Autowired |
|||
ActorSystemContext ctx; |
|||
|
|||
@MockBean |
|||
private TbApiUsageStateService apiUsageStateService; |
|||
|
|||
@MockBean |
|||
private TbApiUsageClient apiUsageClient; |
|||
|
|||
@MockBean |
|||
private TbServiceInfoProvider serviceInfoProvider; |
|||
|
|||
@MockBean |
|||
private ActorService actorService; |
|||
|
|||
@MockBean |
|||
private ComponentDiscoveryService componentService; |
|||
|
|||
@MockBean |
|||
private DataDecodingEncodingService encodingService; |
|||
|
|||
@MockBean |
|||
private DeviceService deviceService; |
|||
|
|||
@MockBean |
|||
private TbTenantProfileCache tenantProfileCache; |
|||
|
|||
@MockBean |
|||
private TbDeviceProfileCache deviceProfileCache; |
|||
|
|||
@MockBean |
|||
private AssetService assetService; |
|||
|
|||
@MockBean |
|||
private DashboardService dashboardService; |
|||
|
|||
@MockBean |
|||
private TenantService tenantService; |
|||
|
|||
@MockBean |
|||
private TenantProfileService tenantProfileService; |
|||
|
|||
@MockBean |
|||
private CustomerService customerService; |
|||
|
|||
@MockBean |
|||
private UserService userService; |
|||
|
|||
@MockBean |
|||
private RuleChainService ruleChainService; |
|||
|
|||
@MockBean |
|||
private RuleNodeStateService ruleNodeStateService; |
|||
|
|||
@MockBean |
|||
private PartitionService partitionService; |
|||
|
|||
@MockBean |
|||
private TbClusterService clusterService; |
|||
|
|||
@MockBean |
|||
private TimeseriesService tsService; |
|||
|
|||
@MockBean |
|||
private AttributesService attributesService; |
|||
|
|||
@MockBean |
|||
private EventService eventService; |
|||
|
|||
@MockBean |
|||
private RelationService relationService; |
|||
|
|||
@MockBean |
|||
private AuditLogService auditLogService; |
|||
|
|||
@MockBean |
|||
private EntityViewService entityViewService; |
|||
|
|||
@MockBean |
|||
private TelemetrySubscriptionService tsSubService; |
|||
|
|||
@MockBean |
|||
private AlarmSubscriptionService alarmService; |
|||
|
|||
@MockBean |
|||
private JsInvokeService jsSandbox; |
|||
|
|||
@MockBean |
|||
private MailExecutorService mailExecutor; |
|||
|
|||
@MockBean |
|||
private SmsExecutorService smsExecutor; |
|||
|
|||
@MockBean |
|||
private DbCallbackExecutorService dbCallbackExecutor; |
|||
|
|||
@MockBean |
|||
private ExternalCallExecutorService externalCallExecutorService; |
|||
|
|||
@MockBean |
|||
private SharedEventLoopGroupService sharedEventLoopGroupService; |
|||
|
|||
@MockBean |
|||
private MailService mailService; |
|||
|
|||
@MockBean |
|||
private SmsService smsService; |
|||
|
|||
@MockBean |
|||
private SmsSenderFactory smsSenderFactory; |
|||
|
|||
@MockBean |
|||
private ClaimDevicesService claimDevicesService; |
|||
|
|||
@MockBean |
|||
private JsInvokeStats jsInvokeStats; |
|||
|
|||
@MockBean |
|||
private DeviceStateService deviceStateService; |
|||
|
|||
@MockBean |
|||
private DeviceSessionCacheService deviceSessionCacheService; |
|||
|
|||
@MockBean |
|||
private TbCoreToTransportService tbCoreToTransportService; |
|||
|
|||
@MockBean |
|||
private TbRuleEngineDeviceRpcService tbRuleEngineDeviceRpcService; |
|||
|
|||
@MockBean |
|||
private TbCoreDeviceRpcService tbCoreDeviceRpcService; |
|||
|
|||
@MockBean |
|||
private EdgeService edgeService; |
|||
|
|||
@MockBean |
|||
private EdgeEventService edgeEventService; |
|||
|
|||
@MockBean |
|||
private EdgeRpcService edgeRpcService; |
|||
|
|||
@MockBean |
|||
private ResourceService resourceService; |
|||
|
|||
@MockBean |
|||
private OtaPackageService otaPackageService; |
|||
|
|||
@MockBean |
|||
private TbRpcService tbRpcService; |
|||
|
|||
@MockBean |
|||
private CassandraCluster cassandraCluster; |
|||
|
|||
@MockBean |
|||
private CassandraBufferedRateReadExecutor cassandraBufferedRateReadExecutor; |
|||
|
|||
@MockBean |
|||
private CassandraBufferedRateWriteExecutor cassandraBufferedRateWriteExecutor; |
|||
|
|||
@MockBean |
|||
private RedisTemplate<String, Object> redisTemplate; |
|||
|
|||
@Test |
|||
void givenCaffeineCache_whenInit_thenIsLocalCacheTrue() { |
|||
assertThat(ctx.getCacheType()).isEqualTo("caffeine"); |
|||
assertThat(ctx.isLocalCacheType()).as("caffeine is the local cache type").isTrue(); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT 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.controller; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.junit.After; |
|||
import org.junit.Before; |
|||
import org.thingsboard.server.queue.memory.InMemoryStorage; |
|||
|
|||
@Slf4j |
|||
public abstract class AbstractInMemoryStorageTest { |
|||
|
|||
@Before |
|||
public void setUpInMemoryStorage() { |
|||
log.info("set up InMemoryStorage"); |
|||
cleanupInMemStorage(); |
|||
} |
|||
|
|||
@After |
|||
public void tearDownInMemoryStorage() { |
|||
log.info("tear down InMemoryStorage"); |
|||
cleanupInMemStorage(); |
|||
} |
|||
|
|||
public static void cleanupInMemStorage() { |
|||
InMemoryStorage.getInstance().cleanup(); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,196 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT 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.controller; |
|||
|
|||
import com.fasterxml.jackson.core.type.TypeReference; |
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import com.fasterxml.jackson.databind.node.ObjectNode; |
|||
import org.junit.After; |
|||
import org.junit.Assert; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.springframework.test.web.servlet.MvcResult; |
|||
import org.thingsboard.common.util.JacksonUtil; |
|||
import org.thingsboard.server.common.data.*; |
|||
import org.thingsboard.server.common.data.page.PageData; |
|||
import org.thingsboard.server.common.data.rpc.Rpc; |
|||
import org.thingsboard.server.common.data.rpc.RpcStatus; |
|||
import org.thingsboard.server.common.data.security.Authority; |
|||
|
|||
import java.util.List; |
|||
|
|||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
|||
|
|||
public abstract class BaseRpcControllerTest extends AbstractControllerTest { |
|||
|
|||
private Tenant savedTenant; |
|||
private User tenantAdmin; |
|||
|
|||
@Before |
|||
public void beforeTest() throws Exception { |
|||
loginSysAdmin(); |
|||
|
|||
Tenant tenant = new Tenant(); |
|||
tenant.setTitle("My tenant"); |
|||
savedTenant = doPost("/api/tenant", tenant, Tenant.class); |
|||
Assert.assertNotNull(savedTenant); |
|||
|
|||
tenantAdmin = new User(); |
|||
tenantAdmin.setAuthority(Authority.TENANT_ADMIN); |
|||
tenantAdmin.setTenantId(savedTenant.getId()); |
|||
tenantAdmin.setEmail("tenant2@thingsboard.org"); |
|||
tenantAdmin.setFirstName("Joe"); |
|||
tenantAdmin.setLastName("Downs"); |
|||
|
|||
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); |
|||
} |
|||
|
|||
@After |
|||
public void afterTest() throws Exception { |
|||
loginSysAdmin(); |
|||
|
|||
doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) |
|||
.andExpect(status().isOk()); |
|||
} |
|||
|
|||
private Device createDefaultDevice() { |
|||
Device device = new Device(); |
|||
device.setName("My device"); |
|||
device.setType("default"); |
|||
|
|||
return device; |
|||
} |
|||
|
|||
private ObjectNode createDefaultRpc() { |
|||
ObjectNode rpc = JacksonUtil.newObjectNode(); |
|||
rpc.put("method", "setGpio"); |
|||
|
|||
ObjectNode params = JacksonUtil.newObjectNode(); |
|||
|
|||
params.put("pin", 7); |
|||
params.put("value", 1); |
|||
|
|||
rpc.set("params", params); |
|||
rpc.put("persistent", true); |
|||
rpc.put("timeout", 5000); |
|||
|
|||
return rpc; |
|||
} |
|||
|
|||
private Rpc getRpcById(String rpcId) throws Exception { |
|||
return doGet("/api/rpc/persistent/" + rpcId, Rpc.class); |
|||
} |
|||
|
|||
private MvcResult removeRpcById(String rpcId) throws Exception { |
|||
return doDelete("/api/rpc/persistent/" + rpcId).andReturn(); |
|||
} |
|||
|
|||
@Test |
|||
public void testSaveRpc() throws Exception { |
|||
Device device = createDefaultDevice(); |
|||
Device savedDevice = doPost("/api/device", device, Device.class); |
|||
|
|||
ObjectNode rpc = createDefaultRpc(); |
|||
String result = doPostAsync( |
|||
"/api/rpc/oneway/" + savedDevice.getId().getId().toString(), |
|||
JacksonUtil.toString(rpc), |
|||
String.class, |
|||
status().isOk() |
|||
); |
|||
String rpcId = JacksonUtil.fromString(result, JsonNode.class) |
|||
.get("rpcId") |
|||
.asText(); |
|||
Rpc savedRpc = getRpcById(rpcId); |
|||
|
|||
Assert.assertNotNull(savedRpc); |
|||
Assert.assertEquals(savedDevice.getId(), savedRpc.getDeviceId()); |
|||
} |
|||
|
|||
@Test |
|||
public void testDeleteRpc() throws Exception { |
|||
Device device = createDefaultDevice(); |
|||
Device savedDevice = doPost("/api/device", device, Device.class); |
|||
|
|||
ObjectNode rpc = createDefaultRpc(); |
|||
String result = doPostAsync( |
|||
"/api/rpc/oneway/" + savedDevice.getId().getId().toString(), |
|||
JacksonUtil.toString(rpc), |
|||
String.class, |
|||
status().isOk() |
|||
); |
|||
String rpcId = JacksonUtil.fromString(result, JsonNode.class) |
|||
.get("rpcId") |
|||
.asText(); |
|||
Rpc savedRpc = getRpcById(rpcId); |
|||
|
|||
MvcResult mvcResult = removeRpcById(savedRpc.getId().getId().toString()); |
|||
MvcResult res = doGet("/api/rpc/persistent/" + rpcId) |
|||
.andExpect(status().isNotFound()) |
|||
.andReturn(); |
|||
|
|||
JsonNode deleteResponse = JacksonUtil.fromString(res.getResponse().getContentAsString(), JsonNode.class); |
|||
Assert.assertEquals(404, deleteResponse.get("status").asInt()); |
|||
|
|||
String url = "/api/rpc/persistent/device/" + savedDevice.getUuidId().toString() |
|||
+ "?" + "page=0" + "&" + |
|||
"pageSize=" + Integer.MAX_VALUE + "&" + |
|||
"rpcStatus=" + RpcStatus.DELETED.name(); |
|||
MvcResult byDeviceResult = doGet(url).andReturn(); |
|||
JsonNode byDeviceResponse = JacksonUtil.fromString(byDeviceResult.getResponse().getContentAsString(), JsonNode.class); |
|||
|
|||
Assert.assertEquals(500, byDeviceResponse.get("status").asInt()); |
|||
} |
|||
|
|||
@Test |
|||
public void testGetRpcsByDeviceId() throws Exception { |
|||
Device device = createDefaultDevice(); |
|||
Device savedDevice = doPost("/api/device", device, Device.class); |
|||
|
|||
ObjectNode rpc = createDefaultRpc(); |
|||
|
|||
String result = doPostAsync( |
|||
"/api/rpc/oneway/" + savedDevice.getId().getId().toString(), |
|||
JacksonUtil.toString(rpc), |
|||
String.class, |
|||
status().isOk() |
|||
); |
|||
String rpcId = JacksonUtil.fromString(result, JsonNode.class) |
|||
.get("rpcId") |
|||
.asText(); |
|||
|
|||
String url = "/api/rpc/persistent/device/" + savedDevice.getId().getId() |
|||
+ "?" + "page=0" + "&" + |
|||
"pageSize=" + Integer.MAX_VALUE + "&" + |
|||
"rpcStatus=" + RpcStatus.QUEUED; |
|||
|
|||
MvcResult byDeviceResult = doGetAsync(url).andReturn(); |
|||
|
|||
List<Rpc> byDeviceRpcs = JacksonUtil.fromString( |
|||
byDeviceResult |
|||
.getResponse() |
|||
.getContentAsString(), |
|||
new TypeReference<PageData<Rpc>>() {} |
|||
).getData(); |
|||
|
|||
|
|||
boolean found = byDeviceRpcs.stream().anyMatch(r -> |
|||
r.getUuidId().toString().equals(rpcId) |
|||
&& r.getDeviceId().equals(savedDevice.getId()) |
|||
); |
|||
|
|||
Assert.assertTrue(found); |
|||
} |
|||
} |
|||
@ -0,0 +1,226 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.mqtt.credentials.sql; |
|||
|
|||
import com.fasterxml.jackson.core.type.TypeReference; |
|||
import org.apache.commons.lang3.RandomStringUtils; |
|||
import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
|||
import org.eclipse.paho.client.mqttv3.MqttConnectOptions; |
|||
import org.eclipse.paho.client.mqttv3.MqttException; |
|||
import org.eclipse.paho.client.mqttv3.MqttSecurityException; |
|||
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; |
|||
import org.junit.After; |
|||
import org.junit.Assert; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.common.util.JacksonUtil; |
|||
import org.thingsboard.server.common.data.Device; |
|||
import org.thingsboard.server.common.data.StringUtils; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.common.data.User; |
|||
import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; |
|||
import org.thingsboard.server.common.data.device.profile.MqttTopics; |
|||
import org.thingsboard.server.common.data.security.Authority; |
|||
import org.thingsboard.server.common.data.security.DeviceCredentials; |
|||
import org.thingsboard.server.common.data.security.DeviceCredentialsType; |
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
import org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest; |
|||
|
|||
import java.util.Arrays; |
|||
import java.util.HashSet; |
|||
import java.util.List; |
|||
import java.util.Set; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
import static org.junit.Assert.assertNotNull; |
|||
import static org.junit.Assert.assertNull; |
|||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
|||
|
|||
@DaoSqlTest |
|||
public class BasicMqttCredentialsTest extends AbstractMqttIntegrationTest { |
|||
|
|||
public static final String CLIENT_ID = "ClientId"; |
|||
public static final String USER_NAME1 = "UserName1"; |
|||
public static final String USER_NAME2 = "UserName2"; |
|||
public static final String USER_NAME3 = "UserName3"; |
|||
public static final String PASSWORD = "secret"; |
|||
|
|||
private Device clientIdDevice; |
|||
private Device clientIdAndUserNameDevice1; |
|||
private Device clientIdAndUserNameAndPasswordDevice2; |
|||
private Device clientIdAndUserNameAndPasswordDevice3; |
|||
private Device accessTokenDevice; |
|||
private Device accessToken2Device; |
|||
|
|||
|
|||
@Before |
|||
public void before() throws Exception { |
|||
loginSysAdmin(); |
|||
|
|||
Tenant tenant = new Tenant(); |
|||
tenant.setTitle("My tenant"); |
|||
savedTenant = doPost("/api/tenant", tenant, Tenant.class); |
|||
Assert.assertNotNull(savedTenant); |
|||
|
|||
tenantAdmin = new User(); |
|||
tenantAdmin.setAuthority(Authority.TENANT_ADMIN); |
|||
tenantAdmin.setTenantId(savedTenant.getId()); |
|||
tenantAdmin.setEmail("tenant" + atomicInteger.getAndIncrement() + "@thingsboard.org"); |
|||
tenantAdmin.setFirstName("Joe"); |
|||
tenantAdmin.setLastName("Downs"); |
|||
|
|||
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); |
|||
|
|||
BasicMqttCredentials credValue = new BasicMqttCredentials(); |
|||
credValue.setClientId(CLIENT_ID); |
|||
clientIdDevice = createDevice("clientIdDevice", credValue); |
|||
|
|||
credValue = new BasicMqttCredentials(); |
|||
credValue.setClientId(CLIENT_ID); |
|||
credValue.setUserName(USER_NAME1); |
|||
clientIdAndUserNameDevice1 = createDevice("clientIdAndUserNameDevice", credValue); |
|||
|
|||
credValue = new BasicMqttCredentials(); |
|||
credValue.setClientId(CLIENT_ID); |
|||
credValue.setUserName(USER_NAME2); |
|||
credValue.setPassword(PASSWORD); |
|||
clientIdAndUserNameAndPasswordDevice2 = createDevice("clientIdAndUserNameAndPasswordDevice", credValue); |
|||
|
|||
credValue = new BasicMqttCredentials(); |
|||
credValue.setClientId(CLIENT_ID); |
|||
credValue.setUserName(USER_NAME3); |
|||
credValue.setPassword(PASSWORD); |
|||
clientIdAndUserNameAndPasswordDevice3 = createDevice("clientIdAndUserNameAndPasswordDevice2", credValue); |
|||
|
|||
accessTokenDevice = createDevice("accessTokenDevice", USER_NAME1); |
|||
accessToken2Device = createDevice("accessToken2Device", USER_NAME2); |
|||
} |
|||
|
|||
@Test |
|||
public void testCorrectCredentials() throws Exception { |
|||
// Check that correct devices receive telemetry
|
|||
testTelemetryIsDelivered(accessTokenDevice, getMqttAsyncClient(null, USER_NAME1, null)); |
|||
testTelemetryIsDelivered(clientIdDevice, getMqttAsyncClient(CLIENT_ID, null, null)); |
|||
testTelemetryIsDelivered(clientIdAndUserNameDevice1, getMqttAsyncClient(CLIENT_ID, USER_NAME1, null)); |
|||
testTelemetryIsDelivered(clientIdAndUserNameAndPasswordDevice2, getMqttAsyncClient(CLIENT_ID, USER_NAME2, PASSWORD)); |
|||
|
|||
// Also correct. Random clientId and password, but matches access token
|
|||
testTelemetryIsDelivered(accessToken2Device, getMqttAsyncClient(RandomStringUtils.randomAlphanumeric(10), USER_NAME2, RandomStringUtils.randomAlphanumeric(10))); |
|||
} |
|||
|
|||
@Test(expected = MqttSecurityException.class) |
|||
public void testCorrectClientIdAndUserNameButWrongPassword() throws Exception { |
|||
// Not correct. Correct clientId and username, but wrong password
|
|||
testTelemetryIsNotDelivered(clientIdAndUserNameAndPasswordDevice3, getMqttAsyncClient(CLIENT_ID, USER_NAME3, "WRONG PASSWORD")); |
|||
} |
|||
|
|||
private void testTelemetryIsDelivered(Device device, MqttAsyncClient client) throws Exception { |
|||
testTelemetryIsDelivered(device, client, true); |
|||
} |
|||
|
|||
private void testTelemetryIsNotDelivered(Device device, MqttAsyncClient client) throws Exception { |
|||
testTelemetryIsDelivered(device, client, false); |
|||
} |
|||
|
|||
private void testTelemetryIsDelivered(Device device, MqttAsyncClient client, boolean ok) throws Exception { |
|||
String randomKey = RandomStringUtils.randomAlphanumeric(10); |
|||
List<String> expectedKeys = Arrays.asList(randomKey); |
|||
publishMqttMsg(client, JacksonUtil.toString(JacksonUtil.newObjectNode().put(randomKey, true)).getBytes(), MqttTopics.DEVICE_TELEMETRY_TOPIC); |
|||
|
|||
String deviceId = device.getId().getId().toString(); |
|||
|
|||
long start = System.currentTimeMillis(); |
|||
long end = System.currentTimeMillis() + 5000; |
|||
|
|||
List<String> actualKeys = null; |
|||
while (start <= end) { |
|||
actualKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/timeseries", new TypeReference<>() { |
|||
}); |
|||
if (actualKeys.size() == expectedKeys.size()) { |
|||
break; |
|||
} |
|||
Thread.sleep(100); |
|||
start += 100; |
|||
} |
|||
if (ok) { |
|||
assertNotNull(actualKeys); |
|||
|
|||
Set<String> actualKeySet = new HashSet<>(actualKeys); |
|||
Set<String> expectedKeySet = new HashSet<>(expectedKeys); |
|||
|
|||
assertEquals(expectedKeySet, actualKeySet); |
|||
} else { |
|||
assertNull(actualKeys); |
|||
} |
|||
client.disconnect().waitForCompletion(); |
|||
} |
|||
|
|||
@After |
|||
public void after() throws Exception { |
|||
processAfterTest(); |
|||
} |
|||
|
|||
protected MqttAsyncClient getMqttAsyncClient(String clientId, String username, String password) throws MqttException { |
|||
if (StringUtils.isEmpty(clientId)) { |
|||
clientId = MqttAsyncClient.generateClientId(); |
|||
} |
|||
MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId, new MemoryPersistence()); |
|||
|
|||
MqttConnectOptions options = new MqttConnectOptions(); |
|||
if (StringUtils.isNotEmpty(username)) { |
|||
options.setUserName(username); |
|||
} |
|||
if (StringUtils.isNotEmpty(password)) { |
|||
options.setPassword(password.toCharArray()); |
|||
} |
|||
client.connect(options).waitForCompletion(); |
|||
return client; |
|||
} |
|||
|
|||
private Device createDevice(String deviceName, BasicMqttCredentials clientIdCredValue) throws Exception { |
|||
Device device = new Device(); |
|||
device.setName(deviceName); |
|||
device.setType("default"); |
|||
|
|||
device = doPost("/api/device", device, Device.class); |
|||
|
|||
DeviceCredentials clientIdCred = |
|||
doGet("/api/device/" + device.getId().getId().toString() + "/credentials", DeviceCredentials.class); |
|||
|
|||
clientIdCred.setCredentialsType(DeviceCredentialsType.MQTT_BASIC); |
|||
|
|||
|
|||
clientIdCred.setCredentialsValue(JacksonUtil.toString(clientIdCredValue)); |
|||
doPost("/api/device/credentials", clientIdCred).andExpect(status().isOk()); |
|||
return device; |
|||
} |
|||
|
|||
private Device createDevice(String deviceName, String accessToken) throws Exception { |
|||
Device device = new Device(); |
|||
device.setName(deviceName); |
|||
device.setType("default"); |
|||
|
|||
device = doPost("/api/device", device, Device.class); |
|||
|
|||
DeviceCredentials clientIdCred = |
|||
doGet("/api/device/" + device.getId().getId().toString() + "/credentials", DeviceCredentials.class); |
|||
|
|||
clientIdCred.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN); |
|||
clientIdCred.setCredentialsId(accessToken); |
|||
doPost("/api/device/credentials", clientIdCred).andExpect(status().isOk()); |
|||
return device; |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT 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.common.data.alarm; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
import org.thingsboard.server.common.data.HasTenantId; |
|||
import org.thingsboard.server.common.data.id.AlarmId; |
|||
import org.thingsboard.server.common.data.id.CustomerId; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
|
|||
@Data |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public class EntityAlarm implements HasTenantId { |
|||
|
|||
private TenantId tenantId; |
|||
private EntityId entityId; |
|||
private long createdTime; |
|||
private String alarmType; |
|||
|
|||
private CustomerId customerId; |
|||
private AlarmId alarmId; |
|||
|
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT 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.common.data.rule; |
|||
|
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import lombok.Data; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.jetbrains.annotations.NotNull; |
|||
import org.thingsboard.server.common.data.id.RuleChainId; |
|||
import org.thingsboard.server.common.data.id.RuleNodeId; |
|||
|
|||
import java.util.Set; |
|||
|
|||
@ApiModel |
|||
@Data |
|||
@Slf4j |
|||
public class RuleChainOutputLabelsUsage { |
|||
|
|||
@ApiModelProperty(position = 1, required = true, value = "Rule Chain Id", accessMode = ApiModelProperty.AccessMode.READ_ONLY) |
|||
private RuleChainId ruleChainId; |
|||
@ApiModelProperty(position = 2, required = true, value = "Rule Node Id", accessMode = ApiModelProperty.AccessMode.READ_ONLY) |
|||
private RuleNodeId ruleNodeId; |
|||
|
|||
@ApiModelProperty(position = 3, required = true, value = "Rule Chain Name", accessMode = ApiModelProperty.AccessMode.READ_ONLY) |
|||
private String ruleChainName; |
|||
@ApiModelProperty(position = 4, required = true, value = "Rule Node Name", accessMode = ApiModelProperty.AccessMode.READ_ONLY) |
|||
private String ruleNodeName; |
|||
@ApiModelProperty(position = 5, required = true, value = "Output labels", accessMode = ApiModelProperty.AccessMode.READ_ONLY) |
|||
private Set<String> labels; |
|||
|
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT 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.common.data.rule; |
|||
|
|||
import lombok.AccessLevel; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.id.RuleNodeId; |
|||
|
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* Created by igor on 3/13/18. |
|||
*/ |
|||
@Data |
|||
@AllArgsConstructor(access = AccessLevel.PRIVATE) |
|||
public class RuleChainUpdateResult { |
|||
|
|||
private final boolean success; |
|||
private final List<RuleNodeUpdateResult> updatedRuleNodes; |
|||
|
|||
public static RuleChainUpdateResult failed(){ |
|||
return new RuleChainUpdateResult(false, null); |
|||
} |
|||
|
|||
public static RuleChainUpdateResult successful(List<RuleNodeUpdateResult> updatedRuleNodes){ |
|||
return new RuleChainUpdateResult(true, updatedRuleNodes); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,95 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT 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.common.msg; |
|||
|
|||
import org.thingsboard.server.common.data.id.RuleChainId; |
|||
import org.thingsboard.server.common.data.id.RuleNodeId; |
|||
import org.thingsboard.server.common.msg.gen.MsgProtos; |
|||
|
|||
import java.io.Serializable; |
|||
import java.util.LinkedList; |
|||
import java.util.List; |
|||
import java.util.UUID; |
|||
import java.util.concurrent.atomic.AtomicInteger; |
|||
|
|||
/** |
|||
* Created by ashvayka on 13.01.18. |
|||
*/ |
|||
public final class TbMsgProcessingCtx implements Serializable { |
|||
|
|||
private final AtomicInteger ruleNodeExecCounter; |
|||
private volatile LinkedList<TbMsgProcessingStackItem> stack; |
|||
|
|||
public TbMsgProcessingCtx() { |
|||
this(0); |
|||
} |
|||
|
|||
public TbMsgProcessingCtx(int ruleNodeExecCounter) { |
|||
this(ruleNodeExecCounter, null); |
|||
} |
|||
|
|||
protected TbMsgProcessingCtx(int ruleNodeExecCounter, LinkedList<TbMsgProcessingStackItem> stack) { |
|||
this.ruleNodeExecCounter = new AtomicInteger(ruleNodeExecCounter); |
|||
this.stack = stack; |
|||
} |
|||
|
|||
public int getAndIncrementRuleNodeCounter() { |
|||
return ruleNodeExecCounter.getAndIncrement(); |
|||
} |
|||
|
|||
public TbMsgProcessingCtx copy() { |
|||
if (stack == null || stack.isEmpty()) { |
|||
return new TbMsgProcessingCtx(ruleNodeExecCounter.get()); |
|||
} else { |
|||
return new TbMsgProcessingCtx(ruleNodeExecCounter.get(), new LinkedList<>(stack)); |
|||
} |
|||
} |
|||
|
|||
public void push(RuleChainId ruleChainId, RuleNodeId ruleNodeId) { |
|||
if (stack == null) { |
|||
stack = new LinkedList<>(); |
|||
} |
|||
stack.add(new TbMsgProcessingStackItem(ruleChainId, ruleNodeId)); |
|||
} |
|||
|
|||
public TbMsgProcessingStackItem pop() { |
|||
return !stack.isEmpty() ? stack.removeLast() : null; |
|||
} |
|||
|
|||
public static TbMsgProcessingCtx fromProto(MsgProtos.TbMsgProcessingCtxProto ctx) { |
|||
int ruleNodeExecCounter = ctx.getRuleNodeExecCounter(); |
|||
if (ctx.getStackCount() > 0) { |
|||
LinkedList<TbMsgProcessingStackItem> stack = new LinkedList<>(); |
|||
for (MsgProtos.TbMsgProcessingStackItemProto item : ctx.getStackList()) { |
|||
stack.add(TbMsgProcessingStackItem.fromProto(item)); |
|||
} |
|||
return new TbMsgProcessingCtx(ruleNodeExecCounter, stack); |
|||
} else { |
|||
return new TbMsgProcessingCtx(ruleNodeExecCounter); |
|||
} |
|||
} |
|||
|
|||
public MsgProtos.TbMsgProcessingCtxProto toProto() { |
|||
var ctxBuilder = MsgProtos.TbMsgProcessingCtxProto.newBuilder(); |
|||
ctxBuilder.setRuleNodeExecCounter(ruleNodeExecCounter.get()); |
|||
if (stack != null) { |
|||
for (TbMsgProcessingStackItem item : stack) { |
|||
ctxBuilder.addStack(item.toProto()); |
|||
} |
|||
} |
|||
return ctxBuilder.build(); |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT 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.common.msg; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.id.RuleChainId; |
|||
import org.thingsboard.server.common.data.id.RuleNodeId; |
|||
import org.thingsboard.server.common.msg.gen.MsgProtos; |
|||
|
|||
import java.util.UUID; |
|||
|
|||
@Data |
|||
public class TbMsgProcessingStackItem { |
|||
|
|||
private final RuleChainId ruleChainId; |
|||
private final RuleNodeId ruleNodeId; |
|||
|
|||
MsgProtos.TbMsgProcessingStackItemProto toProto() { |
|||
return MsgProtos.TbMsgProcessingStackItemProto.newBuilder() |
|||
.setRuleChainIdMSB(ruleChainId.getId().getMostSignificantBits()) |
|||
.setRuleChainIdLSB(ruleChainId.getId().getLeastSignificantBits()) |
|||
.setRuleNodeIdMSB(ruleNodeId.getId().getMostSignificantBits()) |
|||
.setRuleNodeIdLSB(ruleNodeId.getId().getLeastSignificantBits()) |
|||
.build(); |
|||
} |
|||
|
|||
static TbMsgProcessingStackItem fromProto(MsgProtos.TbMsgProcessingStackItemProto item){ |
|||
return new TbMsgProcessingStackItem( |
|||
new RuleChainId(new UUID(item.getRuleChainIdMSB(), item.getRuleChainIdLSB())), |
|||
new RuleNodeId(new UUID(item.getRuleNodeIdMSB(), item.getRuleNodeIdLSB())) |
|||
); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.queue.memory; |
|||
|
|||
import org.junit.After; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.queue.TbQueueMsg; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThat; |
|||
import static org.mockito.Mockito.mock; |
|||
|
|||
public class InMemoryStorageTest { |
|||
|
|||
InMemoryStorage storage = InMemoryStorage.getInstance(); |
|||
|
|||
@Before |
|||
public void setUp() { |
|||
storage.cleanup(); |
|||
} |
|||
|
|||
@After |
|||
public void tearDown() { |
|||
storage.cleanup(); |
|||
} |
|||
|
|||
@Test |
|||
public void givenStorage_whenGetLagTotal_thenReturnInteger() throws InterruptedException { |
|||
assertThat(storage.getLagTotal()).isEqualTo(0); |
|||
storage.put("main", mock(TbQueueMsg.class)); |
|||
assertThat(storage.getLagTotal()).isEqualTo(1); |
|||
storage.put("main", mock(TbQueueMsg.class)); |
|||
assertThat(storage.getLagTotal()).isEqualTo(2); |
|||
storage.put("hp", mock(TbQueueMsg.class)); |
|||
assertThat(storage.getLagTotal()).isEqualTo(3); |
|||
storage.get("main"); |
|||
assertThat(storage.getLagTotal()).isEqualTo(1); |
|||
storage.cleanup(); |
|||
assertThat(storage.getLagTotal()).isEqualTo(0); |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue