307 changed files with 10479 additions and 4897 deletions
@ -0,0 +1,46 @@ |
|||||
|
/** |
||||
|
* 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; |
||||
|
|
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.common.data.edge.Edge; |
||||
|
import org.thingsboard.server.gen.edge.v1.EdgeConfiguration; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
|
||||
|
@Component |
||||
|
@TbCoreComponent |
||||
|
public class EdgeMsgConstructor { |
||||
|
|
||||
|
public EdgeConfiguration constructEdgeConfiguration(Edge edge) { |
||||
|
EdgeConfiguration.Builder builder = EdgeConfiguration.newBuilder() |
||||
|
.setEdgeIdMSB(edge.getId().getId().getMostSignificantBits()) |
||||
|
.setEdgeIdLSB(edge.getId().getId().getLeastSignificantBits()) |
||||
|
.setTenantIdMSB(edge.getTenantId().getId().getMostSignificantBits()) |
||||
|
.setTenantIdLSB(edge.getTenantId().getId().getLeastSignificantBits()) |
||||
|
.setName(edge.getName()) |
||||
|
.setType(edge.getType()) |
||||
|
.setRoutingKey(edge.getRoutingKey()) |
||||
|
.setSecret(edge.getSecret()) |
||||
|
.setAdditionalInfo(JacksonUtil.toString(edge.getAdditionalInfo())) |
||||
|
.setCloudType("CE"); |
||||
|
if (edge.getCustomerId() != null) { |
||||
|
builder.setCustomerIdMSB(edge.getCustomerId().getId().getMostSignificantBits()) |
||||
|
.setCustomerIdLSB(edge.getCustomerId().getId().getLeastSignificantBits()); |
||||
|
} |
||||
|
return builder.build(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,47 @@ |
|||||
|
/** |
||||
|
* 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.fetch; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
import org.thingsboard.server.common.data.EdgeUtils; |
||||
|
import org.thingsboard.server.common.data.edge.Edge; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEvent; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventActionType; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventType; |
||||
|
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.dao.device.DeviceService; |
||||
|
|
||||
|
@AllArgsConstructor |
||||
|
@Slf4j |
||||
|
public class DevicesEdgeEventFetcher extends BasePageableEdgeEventFetcher<Device> { |
||||
|
|
||||
|
private final DeviceService deviceService; |
||||
|
|
||||
|
@Override |
||||
|
PageData<Device> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) { |
||||
|
return deviceService.findDevicesByTenantIdAndEdgeId(tenantId, edge.getId(), pageLink); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, Device device) { |
||||
|
return EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, |
||||
|
EdgeEventActionType.ADDED, device.getId(), null); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,47 @@ |
|||||
|
/** |
||||
|
* 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.fetch; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.thingsboard.server.common.data.EdgeUtils; |
||||
|
import org.thingsboard.server.common.data.EntityView; |
||||
|
import org.thingsboard.server.common.data.edge.Edge; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEvent; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventActionType; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventType; |
||||
|
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.dao.entityview.EntityViewService; |
||||
|
|
||||
|
@AllArgsConstructor |
||||
|
@Slf4j |
||||
|
public class EntityViewsEdgeEventFetcher extends BasePageableEdgeEventFetcher<EntityView> { |
||||
|
|
||||
|
private final EntityViewService entityViewService; |
||||
|
|
||||
|
@Override |
||||
|
PageData<EntityView> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) { |
||||
|
return entityViewService.findEntityViewsByTenantIdAndEdgeId(tenantId, edge.getId(), pageLink); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, EntityView entityView) { |
||||
|
return EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ENTITY_VIEW, |
||||
|
EdgeEventActionType.ADDED, entityView.getId(), null); |
||||
|
} |
||||
|
} |
||||
@ -1,235 +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.processor; |
|
||||
|
|
||||
import com.google.common.util.concurrent.Futures; |
|
||||
import com.google.common.util.concurrent.ListenableFuture; |
|
||||
import lombok.extern.slf4j.Slf4j; |
|
||||
import org.springframework.stereotype.Component; |
|
||||
import org.thingsboard.server.common.data.Device; |
|
||||
import org.thingsboard.server.common.data.EdgeUtils; |
|
||||
import org.thingsboard.server.common.data.edge.Edge; |
|
||||
import org.thingsboard.server.common.data.edge.EdgeEvent; |
|
||||
import org.thingsboard.server.common.data.edge.EdgeEventActionType; |
|
||||
import org.thingsboard.server.common.data.edge.EdgeEventType; |
|
||||
import org.thingsboard.server.common.data.id.CustomerId; |
|
||||
import org.thingsboard.server.common.data.id.DeviceId; |
|
||||
import org.thingsboard.server.common.data.id.EdgeId; |
|
||||
import org.thingsboard.server.common.data.id.EntityId; |
|
||||
import org.thingsboard.server.common.data.id.EntityIdFactory; |
|
||||
import org.thingsboard.server.common.data.id.RuleChainId; |
|
||||
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.rule.RuleChain; |
|
||||
import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; |
|
||||
import org.thingsboard.server.gen.edge.v1.DeviceCredentialsRequestMsg; |
|
||||
import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; |
|
||||
import org.thingsboard.server.gen.edge.v1.DownlinkMsg; |
|
||||
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; |
|
||||
import org.thingsboard.server.gen.transport.TransportProtos; |
|
||||
import org.thingsboard.server.queue.util.TbCoreComponent; |
|
||||
|
|
||||
import java.util.ArrayList; |
|
||||
import java.util.List; |
|
||||
import java.util.UUID; |
|
||||
|
|
||||
@Component |
|
||||
@Slf4j |
|
||||
@TbCoreComponent |
|
||||
public class EntityEdgeProcessor extends BaseEdgeProcessor { |
|
||||
|
|
||||
public DownlinkMsg processEntityMergeRequestMessageToEdge(Edge edge, EdgeEvent edgeEvent) { |
|
||||
DownlinkMsg downlinkMsg = null; |
|
||||
if (EdgeEventType.DEVICE.equals(edgeEvent.getType())) { |
|
||||
DeviceId deviceId = new DeviceId(edgeEvent.getEntityId()); |
|
||||
Device device = deviceService.findDeviceById(edge.getTenantId(), deviceId); |
|
||||
CustomerId customerId = getCustomerIdIfEdgeAssignedToCustomer(device, edge); |
|
||||
String conflictName = null; |
|
||||
if(edgeEvent.getBody() != null) { |
|
||||
conflictName = edgeEvent.getBody().get("conflictName").asText(); |
|
||||
} |
|
||||
DeviceUpdateMsg deviceUpdateMsg = deviceMsgConstructor |
|
||||
.constructDeviceUpdatedMsg(UpdateMsgType.ENTITY_MERGE_RPC_MESSAGE, device, customerId, conflictName); |
|
||||
downlinkMsg = DownlinkMsg.newBuilder() |
|
||||
.setDownlinkMsgId(EdgeUtils.nextPositiveInt()) |
|
||||
.addDeviceUpdateMsg(deviceUpdateMsg) |
|
||||
.build(); |
|
||||
} |
|
||||
return downlinkMsg; |
|
||||
} |
|
||||
|
|
||||
public DownlinkMsg processCredentialsRequestMessageToEdge(EdgeEvent edgeEvent) { |
|
||||
DownlinkMsg downlinkMsg = null; |
|
||||
if (EdgeEventType.DEVICE.equals(edgeEvent.getType())) { |
|
||||
DeviceId deviceId = new DeviceId(edgeEvent.getEntityId()); |
|
||||
DeviceCredentialsRequestMsg deviceCredentialsRequestMsg = DeviceCredentialsRequestMsg.newBuilder() |
|
||||
.setDeviceIdMSB(deviceId.getId().getMostSignificantBits()) |
|
||||
.setDeviceIdLSB(deviceId.getId().getLeastSignificantBits()) |
|
||||
.build(); |
|
||||
DownlinkMsg.Builder builder = DownlinkMsg.newBuilder() |
|
||||
.setDownlinkMsgId(EdgeUtils.nextPositiveInt()) |
|
||||
.addDeviceCredentialsRequestMsg(deviceCredentialsRequestMsg); |
|
||||
downlinkMsg = builder.build(); |
|
||||
} |
|
||||
return downlinkMsg; |
|
||||
} |
|
||||
|
|
||||
public ListenableFuture<Void> processEntityNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { |
|
||||
EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()); |
|
||||
EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType()); |
|
||||
EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type, |
|
||||
new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB())); |
|
||||
EdgeId edgeId = safeGetEdgeId(edgeNotificationMsg); |
|
||||
switch (actionType) { |
|
||||
case ADDED: // used only for USER entity
|
|
||||
case UPDATED: |
|
||||
case CREDENTIALS_UPDATED: |
|
||||
return pushNotificationToAllRelatedEdges(tenantId, entityId, type, actionType); |
|
||||
case ASSIGNED_TO_CUSTOMER: |
|
||||
case UNASSIGNED_FROM_CUSTOMER: |
|
||||
return pushNotificationToAllRelatedCustomerEdges(tenantId, edgeNotificationMsg, entityId, actionType, type); |
|
||||
case DELETED: |
|
||||
if (edgeId != null) { |
|
||||
return saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null); |
|
||||
} else { |
|
||||
return pushNotificationToAllRelatedEdges(tenantId, entityId, type, actionType); |
|
||||
} |
|
||||
case ASSIGNED_TO_EDGE: |
|
||||
case UNASSIGNED_FROM_EDGE: |
|
||||
ListenableFuture<Void> future = saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null); |
|
||||
return Futures.transformAsync(future, unused -> { |
|
||||
if (type.equals(EdgeEventType.RULE_CHAIN)) { |
|
||||
return updateDependentRuleChains(tenantId, new RuleChainId(entityId.getId()), edgeId); |
|
||||
} else { |
|
||||
return Futures.immediateFuture(null); |
|
||||
} |
|
||||
}, dbCallbackExecutorService); |
|
||||
default: |
|
||||
return Futures.immediateFuture(null); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private ListenableFuture<Void> pushNotificationToAllRelatedCustomerEdges(TenantId tenantId, |
|
||||
TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, |
|
||||
EntityId entityId, |
|
||||
EdgeEventActionType actionType, |
|
||||
EdgeEventType type) { |
|
||||
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE); |
|
||||
PageData<EdgeId> pageData; |
|
||||
List<ListenableFuture<Void>> futures = new ArrayList<>(); |
|
||||
do { |
|
||||
pageData = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId, pageLink); |
|
||||
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { |
|
||||
for (EdgeId relatedEdgeId : pageData.getData()) { |
|
||||
try { |
|
||||
CustomerId customerId = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class); |
|
||||
ListenableFuture<Edge> future = edgeService.findEdgeByIdAsync(tenantId, relatedEdgeId); |
|
||||
futures.add(Futures.transformAsync(future, edge -> { |
|
||||
if (edge != null && edge.getCustomerId() != null && |
|
||||
!edge.getCustomerId().isNullUid() && edge.getCustomerId().equals(customerId)) { |
|
||||
return saveEdgeEvent(tenantId, relatedEdgeId, type, actionType, entityId, null); |
|
||||
} else { |
|
||||
return Futures.immediateFuture(null); |
|
||||
} |
|
||||
}, dbCallbackExecutorService)); |
|
||||
} catch (Exception e) { |
|
||||
log.error("Can't parse customer id from entity body [{}]", edgeNotificationMsg, e); |
|
||||
return Futures.immediateFailedFuture(e); |
|
||||
} |
|
||||
} |
|
||||
if (pageData.hasNext()) { |
|
||||
pageLink = pageLink.nextPageLink(); |
|
||||
} |
|
||||
} |
|
||||
} while (pageData != null && pageData.hasNext()); |
|
||||
return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService); |
|
||||
} |
|
||||
|
|
||||
private EdgeId safeGetEdgeId(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { |
|
||||
if (edgeNotificationMsg.getEdgeIdMSB() != 0 && edgeNotificationMsg.getEdgeIdLSB() != 0) { |
|
||||
return new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB())); |
|
||||
} else { |
|
||||
return null; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private ListenableFuture<Void> pushNotificationToAllRelatedEdges(TenantId tenantId, EntityId entityId, EdgeEventType type, EdgeEventActionType actionType) { |
|
||||
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE); |
|
||||
PageData<EdgeId> pageData; |
|
||||
List<ListenableFuture<Void>> futures = new ArrayList<>(); |
|
||||
do { |
|
||||
pageData = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId, pageLink); |
|
||||
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { |
|
||||
for (EdgeId relatedEdgeId : pageData.getData()) { |
|
||||
futures.add(saveEdgeEvent(tenantId, relatedEdgeId, type, actionType, entityId, null)); |
|
||||
} |
|
||||
if (pageData.hasNext()) { |
|
||||
pageLink = pageLink.nextPageLink(); |
|
||||
} |
|
||||
} |
|
||||
} while (pageData != null && pageData.hasNext()); |
|
||||
return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService); |
|
||||
} |
|
||||
|
|
||||
private ListenableFuture<Void> updateDependentRuleChains(TenantId tenantId, RuleChainId processingRuleChainId, EdgeId edgeId) { |
|
||||
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE); |
|
||||
PageData<RuleChain> pageData; |
|
||||
List<ListenableFuture<Void>> futures = new ArrayList<>(); |
|
||||
do { |
|
||||
pageData = ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, pageLink); |
|
||||
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { |
|
||||
for (RuleChain ruleChain : pageData.getData()) { |
|
||||
if (!ruleChain.getId().equals(processingRuleChainId)) { |
|
||||
List<RuleChainConnectionInfo> connectionInfos = |
|
||||
ruleChainService.loadRuleChainMetaData(ruleChain.getTenantId(), ruleChain.getId()).getRuleChainConnections(); |
|
||||
if (connectionInfos != null && !connectionInfos.isEmpty()) { |
|
||||
for (RuleChainConnectionInfo connectionInfo : connectionInfos) { |
|
||||
if (connectionInfo.getTargetRuleChainId().equals(processingRuleChainId)) { |
|
||||
futures.add(saveEdgeEvent(tenantId, |
|
||||
edgeId, |
|
||||
EdgeEventType.RULE_CHAIN_METADATA, |
|
||||
EdgeEventActionType.UPDATED, |
|
||||
ruleChain.getId(), |
|
||||
null)); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
if (pageData.hasNext()) { |
|
||||
pageLink = pageLink.nextPageLink(); |
|
||||
} |
|
||||
} |
|
||||
} while (pageData != null && pageData.hasNext()); |
|
||||
return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService); |
|
||||
} |
|
||||
|
|
||||
public ListenableFuture<Void> processEntityNotificationForAllEdges(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { |
|
||||
EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()); |
|
||||
EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType()); |
|
||||
EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type, new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB())); |
|
||||
switch (actionType) { |
|
||||
case ADDED: |
|
||||
case UPDATED: |
|
||||
case DELETED: |
|
||||
return processActionForAllEdges(tenantId, type, actionType, entityId); |
|
||||
default: |
|
||||
return Futures.immediateFuture(null); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@ -1,249 +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.script; |
|
||||
|
|
||||
import com.google.common.hash.Hashing; |
|
||||
import com.google.common.util.concurrent.Futures; |
|
||||
import com.google.common.util.concurrent.ListenableFuture; |
|
||||
import com.google.common.util.concurrent.MoreExecutors; |
|
||||
import lombok.Getter; |
|
||||
import lombok.extern.slf4j.Slf4j; |
|
||||
import org.springframework.beans.factory.annotation.Value; |
|
||||
import org.springframework.data.util.Pair; |
|
||||
import org.thingsboard.common.util.ThingsBoardThreadFactory; |
|
||||
import org.thingsboard.server.common.data.ApiUsageRecordKey; |
|
||||
import org.thingsboard.server.common.data.id.CustomerId; |
|
||||
import org.thingsboard.server.common.data.id.TenantId; |
|
||||
import org.thingsboard.server.queue.usagestats.TbApiUsageClient; |
|
||||
import org.thingsboard.server.service.apiusage.TbApiUsageStateService; |
|
||||
|
|
||||
import java.util.Map; |
|
||||
import java.util.UUID; |
|
||||
import java.util.concurrent.ConcurrentHashMap; |
|
||||
import java.util.concurrent.Executors; |
|
||||
import java.util.concurrent.ScheduledExecutorService; |
|
||||
import java.util.concurrent.atomic.AtomicInteger; |
|
||||
|
|
||||
import static java.lang.String.format; |
|
||||
|
|
||||
/** |
|
||||
* Created by ashvayka on 26.09.18. |
|
||||
*/ |
|
||||
@Slf4j |
|
||||
@SuppressWarnings("UnstableApiUsage") |
|
||||
public abstract class AbstractJsInvokeService implements JsInvokeService { |
|
||||
|
|
||||
private final TbApiUsageStateService apiUsageStateService; |
|
||||
private final TbApiUsageClient apiUsageClient; |
|
||||
protected ScheduledExecutorService timeoutExecutorService; |
|
||||
|
|
||||
protected final Map<UUID, Pair<String, String>> scriptIdToNameAndHashMap = new ConcurrentHashMap<>(); |
|
||||
protected final Map<UUID, DisableListInfo> disabledFunctions = new ConcurrentHashMap<>(); |
|
||||
|
|
||||
@Getter |
|
||||
@Value("${js.max_total_args_size:100000}") |
|
||||
private long maxTotalArgsSize; |
|
||||
@Getter |
|
||||
@Value("${js.max_result_size:300000}") |
|
||||
private long maxResultSize; |
|
||||
@Getter |
|
||||
@Value("${js.max_script_body_size:50000}") |
|
||||
private long maxScriptBodySize; |
|
||||
|
|
||||
protected AbstractJsInvokeService(TbApiUsageStateService apiUsageStateService, TbApiUsageClient apiUsageClient) { |
|
||||
this.apiUsageStateService = apiUsageStateService; |
|
||||
this.apiUsageClient = apiUsageClient; |
|
||||
} |
|
||||
|
|
||||
public void init(long maxRequestsTimeout) { |
|
||||
if (maxRequestsTimeout > 0) { |
|
||||
timeoutExecutorService = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("nashorn-js-timeout")); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public void stop() { |
|
||||
if (timeoutExecutorService != null) { |
|
||||
timeoutExecutorService.shutdownNow(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public ListenableFuture<UUID> eval(TenantId tenantId, JsScriptType scriptType, String scriptBody, String... argNames) { |
|
||||
if (apiUsageStateService.getApiUsageState(tenantId).isJsExecEnabled()) { |
|
||||
if (scriptBodySizeExceeded(scriptBody)) { |
|
||||
return error(format("Script body exceeds maximum allowed size of %s symbols", getMaxScriptBodySize())); |
|
||||
} |
|
||||
UUID scriptId = UUID.randomUUID(); |
|
||||
String scriptHash = hash(tenantId, scriptBody); |
|
||||
String functionName = constructFunctionName(scriptId, scriptHash); |
|
||||
String jsScript = generateJsScript(scriptType, functionName, scriptBody, argNames); |
|
||||
return doEval(scriptId, scriptHash, functionName, jsScript); |
|
||||
} else { |
|
||||
return error("JS Execution is disabled due to API limits!"); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
protected String constructFunctionName(UUID scriptId, String scriptHash) { |
|
||||
return "invokeInternal_" + scriptId.toString().replace('-', '_'); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public ListenableFuture<String> invokeFunction(TenantId tenantId, CustomerId customerId, UUID scriptId, Object... args) { |
|
||||
if (apiUsageStateService.getApiUsageState(tenantId).isJsExecEnabled()) { |
|
||||
Pair<String, String> nameAndHash = scriptIdToNameAndHashMap.get(scriptId); |
|
||||
if (nameAndHash == null) { |
|
||||
return error("No compiled script found for scriptId: [" + scriptId + "]!"); |
|
||||
} |
|
||||
String functionName = nameAndHash.getFirst(); |
|
||||
String scriptHash = nameAndHash.getSecond(); |
|
||||
if (!isDisabled(scriptId)) { |
|
||||
if (argsSizeExceeded(args)) { |
|
||||
return scriptExecutionError(scriptId, format("Script input arguments exceed maximum allowed total args size of %s symbols", getMaxTotalArgsSize())); |
|
||||
} |
|
||||
apiUsageClient.report(tenantId, customerId, ApiUsageRecordKey.JS_EXEC_COUNT, 1); |
|
||||
return Futures.transformAsync(doInvokeFunction(scriptId, scriptHash, functionName, args), output -> { |
|
||||
String result = output.toString(); |
|
||||
if (resultSizeExceeded(result)) { |
|
||||
return scriptExecutionError(scriptId, format("Script invocation result exceeds maximum allowed size of %s symbols", getMaxResultSize())); |
|
||||
} |
|
||||
return Futures.immediateFuture(result); |
|
||||
}, MoreExecutors.directExecutor()); |
|
||||
} else { |
|
||||
String message = "Script invocation is blocked due to maximum error count " |
|
||||
+ getMaxErrors() + ", scriptId " + scriptId + "!"; |
|
||||
log.warn(message); |
|
||||
return error(message); |
|
||||
} |
|
||||
} else { |
|
||||
return error("JS Execution is disabled due to API limits!"); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public ListenableFuture<Void> release(UUID scriptId) { |
|
||||
Pair<String, String> nameAndHash = scriptIdToNameAndHashMap.get(scriptId); |
|
||||
if (nameAndHash != null) { |
|
||||
try { |
|
||||
scriptIdToNameAndHashMap.remove(scriptId); |
|
||||
disabledFunctions.remove(scriptId); |
|
||||
doRelease(scriptId, nameAndHash.getSecond(), nameAndHash.getFirst()); |
|
||||
} catch (Exception e) { |
|
||||
return Futures.immediateFailedFuture(e); |
|
||||
} |
|
||||
} |
|
||||
return Futures.immediateFuture(null); |
|
||||
} |
|
||||
|
|
||||
protected abstract ListenableFuture<UUID> doEval(UUID scriptId, String scriptHash, String functionName, String scriptBody); |
|
||||
|
|
||||
protected abstract ListenableFuture<Object> doInvokeFunction(UUID scriptId, String scriptHash, String functionName, Object[] args); |
|
||||
|
|
||||
protected abstract void doRelease(UUID scriptId, String scriptHash, String functionName) throws Exception; |
|
||||
|
|
||||
protected abstract int getMaxErrors(); |
|
||||
|
|
||||
protected abstract long getMaxBlacklistDuration(); |
|
||||
|
|
||||
protected String hash(TenantId tenantId, String scriptBody) { |
|
||||
return Hashing.murmur3_128().newHasher() |
|
||||
.putLong(tenantId.getId().getMostSignificantBits()) |
|
||||
.putLong(tenantId.getId().getLeastSignificantBits()) |
|
||||
.putUnencodedChars(scriptBody) |
|
||||
.hash().toString(); |
|
||||
} |
|
||||
|
|
||||
protected void onScriptExecutionError(UUID scriptId, Throwable t, String scriptBody) { |
|
||||
DisableListInfo disableListInfo = disabledFunctions.computeIfAbsent(scriptId, key -> new DisableListInfo()); |
|
||||
log.warn("Script has exception and will increment counter {} on disabledFunctions for id {}, exception {}, cause {}, scriptBody {}", |
|
||||
disableListInfo.get(), scriptId, t, t.getCause(), scriptBody); |
|
||||
disableListInfo.incrementAndGet(); |
|
||||
} |
|
||||
|
|
||||
private boolean scriptBodySizeExceeded(String scriptBody) { |
|
||||
if (getMaxScriptBodySize() <= 0) return false; |
|
||||
return scriptBody.length() > getMaxScriptBodySize(); |
|
||||
} |
|
||||
|
|
||||
private boolean argsSizeExceeded(Object[] args) { |
|
||||
if (getMaxTotalArgsSize() <= 0) return false; |
|
||||
long totalArgsSize = 0; |
|
||||
for (Object arg : args) { |
|
||||
if (arg instanceof CharSequence) { |
|
||||
totalArgsSize += ((CharSequence) arg).length(); |
|
||||
} |
|
||||
} |
|
||||
return totalArgsSize > getMaxTotalArgsSize(); |
|
||||
} |
|
||||
|
|
||||
private boolean resultSizeExceeded(String result) { |
|
||||
if (getMaxResultSize() <= 0) return false; |
|
||||
return result.length() > getMaxResultSize(); |
|
||||
} |
|
||||
|
|
||||
private String generateJsScript(JsScriptType scriptType, String functionName, String scriptBody, String... argNames) { |
|
||||
if (scriptType == JsScriptType.RULE_NODE_SCRIPT) { |
|
||||
return RuleNodeScriptFactory.generateRuleNodeScript(functionName, scriptBody, argNames); |
|
||||
} |
|
||||
throw new RuntimeException("No script factory implemented for scriptType: " + scriptType); |
|
||||
} |
|
||||
|
|
||||
private boolean isDisabled(UUID scriptId) { |
|
||||
DisableListInfo errorCount = disabledFunctions.get(scriptId); |
|
||||
if (errorCount != null) { |
|
||||
if (errorCount.getExpirationTime() <= System.currentTimeMillis()) { |
|
||||
disabledFunctions.remove(scriptId); |
|
||||
return false; |
|
||||
} else { |
|
||||
return errorCount.get() >= getMaxErrors(); |
|
||||
} |
|
||||
} else { |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private <T> ListenableFuture<T> error(String message) { |
|
||||
return Futures.immediateFailedFuture(new RuntimeException(message)); |
|
||||
} |
|
||||
|
|
||||
private <T> ListenableFuture<T> scriptExecutionError(UUID scriptId, String errorMsg) { |
|
||||
RuntimeException error = new RuntimeException(errorMsg); |
|
||||
onScriptExecutionError(scriptId, error, null); |
|
||||
return Futures.immediateFailedFuture(error); |
|
||||
} |
|
||||
|
|
||||
private class DisableListInfo { |
|
||||
private final AtomicInteger counter; |
|
||||
private long expirationTime; |
|
||||
|
|
||||
private DisableListInfo() { |
|
||||
this.counter = new AtomicInteger(0); |
|
||||
} |
|
||||
|
|
||||
public int get() { |
|
||||
return counter.get(); |
|
||||
} |
|
||||
|
|
||||
public int incrementAndGet() { |
|
||||
int result = counter.incrementAndGet(); |
|
||||
expirationTime = System.currentTimeMillis() + getMaxBlacklistDuration(); |
|
||||
return result; |
|
||||
} |
|
||||
|
|
||||
public long getExpirationTime() { |
|
||||
return expirationTime; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,185 +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.script; |
|
||||
|
|
||||
import com.google.common.util.concurrent.FutureCallback; |
|
||||
import com.google.common.util.concurrent.Futures; |
|
||||
import com.google.common.util.concurrent.ListenableFuture; |
|
||||
import com.google.common.util.concurrent.MoreExecutors; |
|
||||
import delight.nashornsandbox.NashornSandbox; |
|
||||
import delight.nashornsandbox.NashornSandboxes; |
|
||||
import lombok.Getter; |
|
||||
import lombok.extern.slf4j.Slf4j; |
|
||||
import org.springframework.beans.factory.annotation.Value; |
|
||||
import org.springframework.data.util.Pair; |
|
||||
import org.springframework.scheduling.annotation.Scheduled; |
|
||||
import org.thingsboard.common.util.ThingsBoardExecutors; |
|
||||
import org.thingsboard.server.queue.usagestats.TbApiUsageClient; |
|
||||
import org.thingsboard.server.service.apiusage.TbApiUsageStateService; |
|
||||
|
|
||||
import javax.annotation.PostConstruct; |
|
||||
import javax.annotation.PreDestroy; |
|
||||
import javax.script.Invocable; |
|
||||
import javax.script.ScriptEngine; |
|
||||
import javax.script.ScriptEngineManager; |
|
||||
import javax.script.ScriptException; |
|
||||
import java.util.UUID; |
|
||||
import java.util.concurrent.ExecutionException; |
|
||||
import java.util.concurrent.ExecutorService; |
|
||||
import java.util.concurrent.TimeUnit; |
|
||||
import java.util.concurrent.atomic.AtomicInteger; |
|
||||
import java.util.concurrent.locks.ReentrantLock; |
|
||||
|
|
||||
@Slf4j |
|
||||
public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeService { |
|
||||
|
|
||||
private NashornSandbox sandbox; |
|
||||
private ScriptEngine engine; |
|
||||
private ExecutorService monitorExecutorService; |
|
||||
|
|
||||
private final AtomicInteger jsPushedMsgs = new AtomicInteger(0); |
|
||||
private final AtomicInteger jsInvokeMsgs = new AtomicInteger(0); |
|
||||
private final AtomicInteger jsEvalMsgs = new AtomicInteger(0); |
|
||||
private final AtomicInteger jsFailedMsgs = new AtomicInteger(0); |
|
||||
private final AtomicInteger jsTimeoutMsgs = new AtomicInteger(0); |
|
||||
private final FutureCallback<UUID> evalCallback = new JsStatCallback<>(jsEvalMsgs, jsTimeoutMsgs, jsFailedMsgs); |
|
||||
private final FutureCallback<Object> invokeCallback = new JsStatCallback<>(jsInvokeMsgs, jsTimeoutMsgs, jsFailedMsgs); |
|
||||
|
|
||||
private final ReentrantLock evalLock = new ReentrantLock(); |
|
||||
|
|
||||
@Getter |
|
||||
private final JsExecutorService jsExecutor; |
|
||||
|
|
||||
@Value("${js.local.max_requests_timeout:0}") |
|
||||
private long maxRequestsTimeout; |
|
||||
|
|
||||
@Value("${js.local.stats.enabled:false}") |
|
||||
private boolean statsEnabled; |
|
||||
|
|
||||
public AbstractNashornJsInvokeService(TbApiUsageStateService apiUsageStateService, TbApiUsageClient apiUsageClient, JsExecutorService jsExecutor) { |
|
||||
super(apiUsageStateService, apiUsageClient); |
|
||||
this.jsExecutor = jsExecutor; |
|
||||
} |
|
||||
|
|
||||
@Scheduled(fixedDelayString = "${js.local.stats.print_interval_ms:10000}") |
|
||||
public void printStats() { |
|
||||
if (statsEnabled) { |
|
||||
int pushedMsgs = jsPushedMsgs.getAndSet(0); |
|
||||
int invokeMsgs = jsInvokeMsgs.getAndSet(0); |
|
||||
int evalMsgs = jsEvalMsgs.getAndSet(0); |
|
||||
int failed = jsFailedMsgs.getAndSet(0); |
|
||||
int timedOut = jsTimeoutMsgs.getAndSet(0); |
|
||||
if (pushedMsgs > 0 || invokeMsgs > 0 || evalMsgs > 0 || failed > 0 || timedOut > 0) { |
|
||||
log.info("Nashorn JS Invoke Stats: pushed [{}] received [{}] invoke [{}] eval [{}] failed [{}] timedOut [{}]", |
|
||||
pushedMsgs, invokeMsgs + evalMsgs, invokeMsgs, evalMsgs, failed, timedOut); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@PostConstruct |
|
||||
public void init() { |
|
||||
super.init(maxRequestsTimeout); |
|
||||
if (useJsSandbox()) { |
|
||||
sandbox = NashornSandboxes.create(); |
|
||||
monitorExecutorService = ThingsBoardExecutors.newWorkStealingPool(getMonitorThreadPoolSize(), "nashorn-js-monitor"); |
|
||||
sandbox.setExecutor(monitorExecutorService); |
|
||||
sandbox.setMaxCPUTime(getMaxCpuTime()); |
|
||||
sandbox.allowNoBraces(false); |
|
||||
sandbox.allowLoadFunctions(true); |
|
||||
sandbox.setMaxPreparedStatements(30); |
|
||||
} else { |
|
||||
ScriptEngineManager factory = new ScriptEngineManager(); |
|
||||
engine = factory.getEngineByName("nashorn"); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@PreDestroy |
|
||||
public void stop() { |
|
||||
super.stop(); |
|
||||
if (monitorExecutorService != null) { |
|
||||
monitorExecutorService.shutdownNow(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
protected abstract boolean useJsSandbox(); |
|
||||
|
|
||||
protected abstract int getMonitorThreadPoolSize(); |
|
||||
|
|
||||
protected abstract long getMaxCpuTime(); |
|
||||
|
|
||||
@Override |
|
||||
protected ListenableFuture<UUID> doEval(UUID scriptId, String scriptHash, String functionName, String jsScript) { |
|
||||
jsPushedMsgs.incrementAndGet(); |
|
||||
ListenableFuture<UUID> result = jsExecutor.executeAsync(() -> { |
|
||||
try { |
|
||||
evalLock.lock(); |
|
||||
try { |
|
||||
if (useJsSandbox()) { |
|
||||
sandbox.eval(jsScript); |
|
||||
} else { |
|
||||
engine.eval(jsScript); |
|
||||
} |
|
||||
} finally { |
|
||||
evalLock.unlock(); |
|
||||
} |
|
||||
scriptIdToNameAndHashMap.put(scriptId, Pair.of(functionName, scriptHash)); |
|
||||
return scriptId; |
|
||||
} catch (Exception e) { |
|
||||
log.debug("Failed to compile JS script: {}", e.getMessage(), e); |
|
||||
throw new ExecutionException(e); |
|
||||
} |
|
||||
}); |
|
||||
if (maxRequestsTimeout > 0) { |
|
||||
result = Futures.withTimeout(result, maxRequestsTimeout, TimeUnit.MILLISECONDS, timeoutExecutorService); |
|
||||
} |
|
||||
Futures.addCallback(result, evalCallback, MoreExecutors.directExecutor()); |
|
||||
return result; |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
protected ListenableFuture<Object> doInvokeFunction(UUID scriptId, String scriptHash, String functionName, Object[] args) { |
|
||||
jsPushedMsgs.incrementAndGet(); |
|
||||
ListenableFuture<Object> result = jsExecutor.executeAsync(() -> { |
|
||||
try { |
|
||||
if (useJsSandbox()) { |
|
||||
return sandbox.getSandboxedInvocable().invokeFunction(functionName, args); |
|
||||
} 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); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
if (maxRequestsTimeout > 0) { |
|
||||
result = Futures.withTimeout(result, maxRequestsTimeout, TimeUnit.MILLISECONDS, timeoutExecutorService); |
|
||||
} |
|
||||
Futures.addCallback(result, invokeCallback, MoreExecutors.directExecutor()); |
|
||||
return result; |
|
||||
} |
|
||||
|
|
||||
protected void doRelease(UUID scriptId, String scriptHash, String functionName) throws ScriptException { |
|
||||
if (useJsSandbox()) { |
|
||||
sandbox.eval(functionName + " = undefined;"); |
|
||||
} else { |
|
||||
engine.eval(functionName + " = undefined;"); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
@ -1,64 +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.script; |
|
||||
|
|
||||
import lombok.Getter; |
|
||||
import lombok.extern.slf4j.Slf4j; |
|
||||
import org.springframework.beans.factory.annotation.Value; |
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
|
||||
import org.springframework.stereotype.Service; |
|
||||
import org.thingsboard.server.queue.usagestats.TbApiUsageClient; |
|
||||
import org.thingsboard.server.service.apiusage.TbApiUsageStateService; |
|
||||
|
|
||||
import java.util.concurrent.TimeUnit; |
|
||||
|
|
||||
@Slf4j |
|
||||
@ConditionalOnProperty(prefix = "js", value = "evaluator", havingValue = "local", matchIfMissing = true) |
|
||||
@Service |
|
||||
public class NashornJsInvokeService extends AbstractNashornJsInvokeService { |
|
||||
|
|
||||
@Value("${js.local.use_js_sandbox}") |
|
||||
private boolean useJsSandbox; |
|
||||
|
|
||||
@Getter |
|
||||
@Value("${js.local.monitor_thread_pool_size}") |
|
||||
private int monitorThreadPoolSize; |
|
||||
|
|
||||
@Getter |
|
||||
@Value("${js.local.max_cpu_time}") |
|
||||
private long maxCpuTime; |
|
||||
|
|
||||
@Getter |
|
||||
@Value("${js.local.max_errors}") |
|
||||
private int maxErrors; |
|
||||
|
|
||||
@Value("${js.local.max_black_list_duration_sec:60}") |
|
||||
private int maxBlackListDurationSec; |
|
||||
|
|
||||
public NashornJsInvokeService(TbApiUsageStateService apiUsageStateService, TbApiUsageClient apiUsageClient, JsExecutorService jsExecutor) { |
|
||||
super(apiUsageStateService, apiUsageClient, jsExecutor); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
protected boolean useJsSandbox() { |
|
||||
return useJsSandbox; |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
protected long getMaxBlacklistDuration() { |
|
||||
return TimeUnit.SECONDS.toMillis(maxBlackListDurationSec); |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,171 @@ |
|||||
|
/** |
||||
|
* 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.script; |
||||
|
|
||||
|
import com.fasterxml.jackson.core.type.TypeReference; |
||||
|
import com.fasterxml.jackson.databind.JsonNode; |
||||
|
import com.google.common.util.concurrent.Futures; |
||||
|
import com.google.common.util.concurrent.ListenableFuture; |
||||
|
import com.google.common.util.concurrent.MoreExecutors; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.script.api.RuleNodeScriptFactory; |
||||
|
import org.thingsboard.script.api.mvel.MvelInvokeService; |
||||
|
import org.thingsboard.server.common.data.StringUtils; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.msg.TbMsg; |
||||
|
import org.thingsboard.server.common.msg.TbMsgMetaData; |
||||
|
|
||||
|
import javax.script.ScriptException; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.Collection; |
||||
|
import java.util.Collections; |
||||
|
import java.util.HashMap; |
||||
|
import java.util.HashSet; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
import java.util.Set; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
|
||||
|
@Slf4j |
||||
|
public class RuleNodeMvelScriptEngine extends RuleNodeScriptEngine<MvelInvokeService, Object> { |
||||
|
|
||||
|
public RuleNodeMvelScriptEngine(TenantId tenantId, MvelInvokeService scriptInvokeService, String script, String... argNames) { |
||||
|
super(tenantId, scriptInvokeService, script, argNames); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected ListenableFuture<Boolean> executeFilterTransform(Object result) { |
||||
|
if (result instanceof Boolean) { |
||||
|
return Futures.immediateFuture((Boolean) result); |
||||
|
} |
||||
|
return wrongResultType(result); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected ListenableFuture<List<TbMsg>> executeUpdateTransform(TbMsg msg, Object result) { |
||||
|
if (result instanceof Map) { |
||||
|
return Futures.immediateFuture(Collections.singletonList(unbindMsg((Map) result, msg))); |
||||
|
} else if (result instanceof Collection) { |
||||
|
List<TbMsg> res = new ArrayList<>(); |
||||
|
for (Object resObject : (Collection) result) { |
||||
|
if (resObject instanceof Map) { |
||||
|
res.add(unbindMsg((Map) result, msg)); |
||||
|
} else { |
||||
|
return wrongResultType(resObject); |
||||
|
} |
||||
|
} |
||||
|
return Futures.immediateFuture(res); |
||||
|
} |
||||
|
return wrongResultType(result); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected ListenableFuture<TbMsg> executeGenerateTransform(TbMsg prevMsg, Object result) { |
||||
|
if (result instanceof Map) { |
||||
|
return Futures.immediateFuture(unbindMsg((Map) result, prevMsg)); |
||||
|
} |
||||
|
return wrongResultType(result); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected ListenableFuture<String> executeToStringTransform(Object result) { |
||||
|
if (result instanceof String) { |
||||
|
return Futures.immediateFuture((String) result); |
||||
|
} else { |
||||
|
return Futures.immediateFuture(JacksonUtil.toString(result)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected ListenableFuture<Set<String>> executeSwitchTransform(Object result) { |
||||
|
if (result instanceof String) { |
||||
|
return Futures.immediateFuture(Collections.singleton((String) result)); |
||||
|
} else if (result instanceof Collection) { |
||||
|
Set<String> res = new HashSet<>(); |
||||
|
for (Object resObject : (Collection) result) { |
||||
|
if (resObject instanceof String) { |
||||
|
res.add((String) resObject); |
||||
|
} else { |
||||
|
return wrongResultType(resObject); |
||||
|
} |
||||
|
} |
||||
|
return Futures.immediateFuture(res); |
||||
|
} |
||||
|
return wrongResultType(result); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public ListenableFuture<JsonNode> executeJsonAsync(TbMsg msg) { |
||||
|
return Futures.transform(executeScriptAsync(msg), JacksonUtil::valueToTree, MoreExecutors.directExecutor()); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected Object convertResult(Object result) { |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected Object[] prepareArgs(TbMsg msg) { |
||||
|
Object[] args = new Object[3]; |
||||
|
if (msg.getData() != null) { |
||||
|
args[0] = JacksonUtil.fromString(msg.getData(), Map.class); |
||||
|
} else { |
||||
|
args[0] = new HashMap<>(); |
||||
|
} |
||||
|
args[1] = new HashMap<>(msg.getMetaData().getData()); |
||||
|
args[2] = msg.getType(); |
||||
|
return args; |
||||
|
} |
||||
|
|
||||
|
private static TbMsg unbindMsg(Map msgData, TbMsg msg) { |
||||
|
String data = null; |
||||
|
Map<String, String> metadata = null; |
||||
|
String messageType = null; |
||||
|
if (msgData.containsKey(RuleNodeScriptFactory.MSG)) { |
||||
|
data = JacksonUtil.toString(msgData.get(RuleNodeScriptFactory.MSG)); |
||||
|
} |
||||
|
if (msgData.containsKey(RuleNodeScriptFactory.METADATA)) { |
||||
|
Object msgMetadataObj = msgData.get(RuleNodeScriptFactory.METADATA); |
||||
|
if (msgMetadataObj instanceof Map) { |
||||
|
metadata = ((Map<?, ?>) msgMetadataObj).entrySet().stream().filter(e -> e.getValue() != null) |
||||
|
.collect(Collectors.toMap(e -> e.getKey().toString(), e -> e.getValue().toString())); |
||||
|
} else { |
||||
|
metadata = JacksonUtil.convertValue(msgMetadataObj, new TypeReference<>() { |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
if (msgData.containsKey(RuleNodeScriptFactory.MSG_TYPE)) { |
||||
|
messageType = msgData.get(RuleNodeScriptFactory.MSG_TYPE).toString(); |
||||
|
} |
||||
|
String newData = data != null ? data : msg.getData(); |
||||
|
TbMsgMetaData newMetadata = metadata != null ? new TbMsgMetaData(metadata) : msg.getMetaData().copy(); |
||||
|
String newMessageType = !StringUtils.isEmpty(messageType) ? messageType : msg.getType(); |
||||
|
return TbMsg.transformMsg(msg, newMessageType, msg.getOriginator(), newMetadata, newData); |
||||
|
} |
||||
|
|
||||
|
private static <T> ListenableFuture<T> wrongResultType(Object result) { |
||||
|
String className = toClassName(result); |
||||
|
log.warn("Wrong result type: {}", className); |
||||
|
return Futures.immediateFailedFuture(new ScriptException("Wrong result type: " + className)); |
||||
|
} |
||||
|
|
||||
|
private static String toClassName(Object result) { |
||||
|
return result != null ? result.getClass().getSimpleName() : "null"; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,133 @@ |
|||||
|
/** |
||||
|
* 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.script; |
||||
|
|
||||
|
import com.google.common.util.concurrent.Futures; |
||||
|
import com.google.common.util.concurrent.ListenableFuture; |
||||
|
import com.google.common.util.concurrent.MoreExecutors; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.thingsboard.rule.engine.api.ScriptEngine; |
||||
|
import org.thingsboard.script.api.ScriptInvokeService; |
||||
|
import org.thingsboard.script.api.ScriptType; |
||||
|
import org.thingsboard.server.common.data.id.CustomerId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.msg.TbMsg; |
||||
|
|
||||
|
import javax.script.ScriptException; |
||||
|
import java.util.List; |
||||
|
import java.util.Set; |
||||
|
import java.util.UUID; |
||||
|
import java.util.concurrent.ExecutionException; |
||||
|
|
||||
|
|
||||
|
@Slf4j |
||||
|
public abstract class RuleNodeScriptEngine<T extends ScriptInvokeService, R> implements ScriptEngine { |
||||
|
|
||||
|
private final T scriptInvokeService; |
||||
|
|
||||
|
private final UUID scriptId; |
||||
|
private final TenantId tenantId; |
||||
|
|
||||
|
public RuleNodeScriptEngine(TenantId tenantId, T scriptInvokeService, String script, String... argNames) { |
||||
|
this.tenantId = tenantId; |
||||
|
this.scriptInvokeService = scriptInvokeService; |
||||
|
try { |
||||
|
this.scriptId = this.scriptInvokeService.eval(tenantId, ScriptType.RULE_NODE_SCRIPT, script, argNames).get(); |
||||
|
} catch (Exception e) { |
||||
|
Throwable t = e; |
||||
|
if (e instanceof ExecutionException) { |
||||
|
t = e.getCause(); |
||||
|
} |
||||
|
throw new IllegalArgumentException("Can't compile script: " + t.getMessage(), t); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected abstract Object[] prepareArgs(TbMsg msg); |
||||
|
|
||||
|
@Override |
||||
|
public ListenableFuture<List<TbMsg>> executeUpdateAsync(TbMsg msg) { |
||||
|
ListenableFuture<R> result = executeScriptAsync(msg); |
||||
|
return Futures.transformAsync(result, |
||||
|
json -> executeUpdateTransform(msg, json), |
||||
|
MoreExecutors.directExecutor()); |
||||
|
} |
||||
|
|
||||
|
protected abstract ListenableFuture<List<TbMsg>> executeUpdateTransform(TbMsg msg, R result); |
||||
|
|
||||
|
@Override |
||||
|
public ListenableFuture<TbMsg> executeGenerateAsync(TbMsg prevMsg) { |
||||
|
return Futures.transformAsync(executeScriptAsync(prevMsg), |
||||
|
result -> executeGenerateTransform(prevMsg, result), |
||||
|
MoreExecutors.directExecutor()); |
||||
|
} |
||||
|
|
||||
|
protected abstract ListenableFuture<TbMsg> executeGenerateTransform(TbMsg prevMsg, R result); |
||||
|
|
||||
|
@Override |
||||
|
public ListenableFuture<String> executeToStringAsync(TbMsg msg) { |
||||
|
return Futures.transformAsync(executeScriptAsync(msg), this::executeToStringTransform, MoreExecutors.directExecutor()); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
public ListenableFuture<Boolean> executeFilterAsync(TbMsg msg) { |
||||
|
return Futures.transformAsync(executeScriptAsync(msg), |
||||
|
this::executeFilterTransform, |
||||
|
MoreExecutors.directExecutor()); |
||||
|
} |
||||
|
|
||||
|
protected abstract ListenableFuture<String> executeToStringTransform(R result); |
||||
|
|
||||
|
protected abstract ListenableFuture<Boolean> executeFilterTransform(R result); |
||||
|
|
||||
|
protected abstract ListenableFuture<Set<String>> executeSwitchTransform(R result); |
||||
|
|
||||
|
@Override |
||||
|
public ListenableFuture<Set<String>> executeSwitchAsync(TbMsg msg) { |
||||
|
return Futures.transformAsync(executeScriptAsync(msg), |
||||
|
this::executeSwitchTransform, |
||||
|
MoreExecutors.directExecutor()); //usually runs in a callbackExecutor
|
||||
|
} |
||||
|
|
||||
|
ListenableFuture<R> executeScriptAsync(TbMsg msg) { |
||||
|
log.trace("execute script async, msg {}", msg); |
||||
|
Object[] inArgs = prepareArgs(msg); |
||||
|
return executeScriptAsync(msg.getCustomerId(), inArgs[0], inArgs[1], inArgs[2]); |
||||
|
} |
||||
|
|
||||
|
ListenableFuture<R> executeScriptAsync(CustomerId customerId, Object... args) { |
||||
|
return Futures.transformAsync(scriptInvokeService.invokeScript(tenantId, customerId, this.scriptId, args), |
||||
|
o -> { |
||||
|
try { |
||||
|
return Futures.immediateFuture(convertResult(o)); |
||||
|
} catch (Exception e) { |
||||
|
if (e.getCause() instanceof ScriptException) { |
||||
|
return Futures.immediateFailedFuture(e.getCause()); |
||||
|
} else if (e.getCause() instanceof RuntimeException) { |
||||
|
return Futures.immediateFailedFuture(new ScriptException(e.getCause().getMessage())); |
||||
|
} else { |
||||
|
return Futures.immediateFailedFuture(new ScriptException(e)); |
||||
|
} |
||||
|
} |
||||
|
}, MoreExecutors.directExecutor()); |
||||
|
} |
||||
|
|
||||
|
public void destroy() { |
||||
|
scriptInvokeService.release(this.scriptId); |
||||
|
} |
||||
|
|
||||
|
protected abstract R convertResult(Object result); |
||||
|
} |
||||
@ -0,0 +1,587 @@ |
|||||
|
/** |
||||
|
* 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.edge; |
||||
|
|
||||
|
import com.fasterxml.jackson.core.JsonProcessingException; |
||||
|
import com.fasterxml.jackson.core.type.TypeReference; |
||||
|
import com.fasterxml.jackson.databind.JsonNode; |
||||
|
import com.google.protobuf.AbstractMessage; |
||||
|
import com.google.protobuf.InvalidProtocolBufferException; |
||||
|
import com.google.protobuf.MessageLite; |
||||
|
import org.junit.After; |
||||
|
import org.junit.Assert; |
||||
|
import org.junit.Before; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.test.context.TestPropertySource; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.cluster.TbClusterService; |
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
import org.thingsboard.server.common.data.DeviceProfile; |
||||
|
import org.thingsboard.server.common.data.OtaPackageInfo; |
||||
|
import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest; |
||||
|
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.alarm.AlarmSeverity; |
||||
|
import org.thingsboard.server.common.data.asset.Asset; |
||||
|
import org.thingsboard.server.common.data.asset.AssetProfile; |
||||
|
import org.thingsboard.server.common.data.device.data.DefaultDeviceConfiguration; |
||||
|
import org.thingsboard.server.common.data.device.data.DeviceData; |
||||
|
import org.thingsboard.server.common.data.device.data.MqttDeviceTransportConfiguration; |
||||
|
import org.thingsboard.server.common.data.device.profile.AlarmCondition; |
||||
|
import org.thingsboard.server.common.data.device.profile.AlarmConditionFilter; |
||||
|
import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey; |
||||
|
import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType; |
||||
|
import org.thingsboard.server.common.data.device.profile.AlarmRule; |
||||
|
import org.thingsboard.server.common.data.device.profile.AllowCreateNewDevicesDeviceProfileProvisionConfiguration; |
||||
|
import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; |
||||
|
import org.thingsboard.server.common.data.device.profile.DeviceProfileData; |
||||
|
import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration; |
||||
|
import org.thingsboard.server.common.data.device.profile.SimpleAlarmConditionSpec; |
||||
|
import org.thingsboard.server.common.data.edge.Edge; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEvent; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventActionType; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventType; |
||||
|
import org.thingsboard.server.common.data.id.DeviceProfileId; |
||||
|
import org.thingsboard.server.common.data.id.EdgeId; |
||||
|
import org.thingsboard.server.common.data.id.RuleChainId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.common.data.query.EntityKeyValueType; |
||||
|
import org.thingsboard.server.common.data.query.FilterPredicateValue; |
||||
|
import org.thingsboard.server.common.data.query.NumericFilterPredicate; |
||||
|
import org.thingsboard.server.common.data.queue.Queue; |
||||
|
import org.thingsboard.server.common.data.rule.RuleChain; |
||||
|
import org.thingsboard.server.common.data.security.Authority; |
||||
|
import org.thingsboard.server.controller.AbstractControllerTest; |
||||
|
import org.thingsboard.server.dao.edge.EdgeEventService; |
||||
|
import org.thingsboard.server.edge.imitator.EdgeImitator; |
||||
|
import org.thingsboard.server.gen.edge.v1.AdminSettingsUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.EdgeConfiguration; |
||||
|
import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataRequestMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; |
||||
|
import org.thingsboard.server.gen.edge.v1.UplinkMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; |
||||
|
import org.thingsboard.server.queue.util.DataDecodingEncodingService; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
import java.util.Optional; |
||||
|
import java.util.TreeMap; |
||||
|
import java.util.UUID; |
||||
|
|
||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; |
||||
|
|
||||
|
@TestPropertySource(properties = { |
||||
|
"edges.enabled=true", |
||||
|
}) |
||||
|
abstract public class AbstractEdgeTest extends AbstractControllerTest { |
||||
|
|
||||
|
private static final String THERMOSTAT_DEVICE_PROFILE_NAME = "Thermostat"; |
||||
|
|
||||
|
protected Tenant savedTenant; |
||||
|
protected TenantId tenantId; |
||||
|
protected User tenantAdmin; |
||||
|
|
||||
|
protected DeviceProfile thermostatDeviceProfile; |
||||
|
|
||||
|
protected EdgeImitator edgeImitator; |
||||
|
protected Edge edge; |
||||
|
|
||||
|
@Autowired |
||||
|
protected EdgeEventService edgeEventService; |
||||
|
|
||||
|
@Autowired |
||||
|
protected DataDecodingEncodingService dataDecodingEncodingService; |
||||
|
|
||||
|
@Autowired |
||||
|
protected TbClusterService clusterService; |
||||
|
|
||||
|
@Before |
||||
|
public void beforeTest() throws Exception { |
||||
|
loginSysAdmin(); |
||||
|
|
||||
|
Tenant tenant = new Tenant(); |
||||
|
tenant.setTitle("My tenant"); |
||||
|
savedTenant = doPost("/api/tenant", tenant, Tenant.class); |
||||
|
tenantId = savedTenant.getId(); |
||||
|
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"); |
||||
|
// sleep 0.5 second to avoid CREDENTIALS updated message for the user
|
||||
|
// user credentials is going to be stored and updated event pushed to edge notification service
|
||||
|
// while service will be processing this event edge could be already added and additional message will be pushed
|
||||
|
Thread.sleep(500); |
||||
|
|
||||
|
installation(); |
||||
|
|
||||
|
edgeImitator = new EdgeImitator("localhost", 7070, edge.getRoutingKey(), edge.getSecret()); |
||||
|
edgeImitator.expectMessageAmount(21); |
||||
|
edgeImitator.connect(); |
||||
|
|
||||
|
requestEdgeRuleChainMetadata(); |
||||
|
|
||||
|
verifyEdgeConnectionAndInitialData(); |
||||
|
} |
||||
|
|
||||
|
private void requestEdgeRuleChainMetadata() throws Exception { |
||||
|
RuleChainId rootRuleChainId = getEdgeRootRuleChainId(); |
||||
|
RuleChainMetadataRequestMsg.Builder builder = RuleChainMetadataRequestMsg.newBuilder() |
||||
|
.setRuleChainIdMSB(rootRuleChainId.getId().getMostSignificantBits()) |
||||
|
.setRuleChainIdLSB(rootRuleChainId.getId().getLeastSignificantBits()); |
||||
|
testAutoGeneratedCodeByProtobuf(builder); |
||||
|
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder() |
||||
|
.addRuleChainMetadataRequestMsg(builder.build()); |
||||
|
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); |
||||
|
} |
||||
|
|
||||
|
private RuleChainId getEdgeRootRuleChainId() throws Exception { |
||||
|
List<RuleChain> edgeRuleChains = doGetTypedWithPageLink("/api/edge/" + edge.getUuidId() + "/ruleChains?", |
||||
|
new TypeReference<PageData<RuleChain>>() {}, new PageLink(100)).getData(); |
||||
|
for (RuleChain edgeRuleChain : edgeRuleChains) { |
||||
|
if (edgeRuleChain.isRoot()) { |
||||
|
return edgeRuleChain.getId(); |
||||
|
} |
||||
|
} |
||||
|
throw new RuntimeException("Root rule chain not found"); |
||||
|
} |
||||
|
|
||||
|
@After |
||||
|
public void afterTest() throws Exception { |
||||
|
loginSysAdmin(); |
||||
|
|
||||
|
doDelete("/api/tenant/" + savedTenant.getUuidId()) |
||||
|
.andExpect(status().isOk()); |
||||
|
|
||||
|
try { |
||||
|
edgeImitator.disconnect(); |
||||
|
} catch (Exception ignored) {} |
||||
|
} |
||||
|
|
||||
|
private void installation() { |
||||
|
edge = doPost("/api/edge", constructEdge("Test Edge", "test"), Edge.class); |
||||
|
|
||||
|
thermostatDeviceProfile = this.createDeviceProfile(THERMOSTAT_DEVICE_PROFILE_NAME, |
||||
|
createMqttDeviceProfileTransportConfiguration(new JsonTransportPayloadConfiguration(), false)); |
||||
|
|
||||
|
extendDeviceProfileData(thermostatDeviceProfile); |
||||
|
thermostatDeviceProfile = doPost("/api/deviceProfile", thermostatDeviceProfile, DeviceProfile.class); |
||||
|
|
||||
|
Device savedDevice = saveDevice("Edge Device 1", THERMOSTAT_DEVICE_PROFILE_NAME); |
||||
|
doPost("/api/edge/" + edge.getUuidId() |
||||
|
+ "/device/" + savedDevice.getUuidId(), Device.class); |
||||
|
|
||||
|
Asset savedAsset = saveAsset("Edge Asset 1"); |
||||
|
doPost("/api/edge/" + edge.getUuidId() |
||||
|
+ "/asset/" + savedAsset.getUuidId(), Asset.class); |
||||
|
} |
||||
|
|
||||
|
protected void extendDeviceProfileData(DeviceProfile deviceProfile) { |
||||
|
DeviceProfileData profileData = deviceProfile.getProfileData(); |
||||
|
List<DeviceProfileAlarm> alarms = new ArrayList<>(); |
||||
|
DeviceProfileAlarm deviceProfileAlarm = new DeviceProfileAlarm(); |
||||
|
deviceProfileAlarm.setAlarmType("High Temperature"); |
||||
|
AlarmRule alarmRule = new AlarmRule(); |
||||
|
alarmRule.setAlarmDetails("Alarm Details"); |
||||
|
AlarmCondition alarmCondition = new AlarmCondition(); |
||||
|
alarmCondition.setSpec(new SimpleAlarmConditionSpec()); |
||||
|
List<AlarmConditionFilter> condition = new ArrayList<>(); |
||||
|
AlarmConditionFilter alarmConditionFilter = new AlarmConditionFilter(); |
||||
|
alarmConditionFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, "temperature")); |
||||
|
NumericFilterPredicate predicate = new NumericFilterPredicate(); |
||||
|
predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); |
||||
|
predicate.setValue(new FilterPredicateValue<>(55.0)); |
||||
|
alarmConditionFilter.setPredicate(predicate); |
||||
|
alarmConditionFilter.setValueType(EntityKeyValueType.NUMERIC); |
||||
|
condition.add(alarmConditionFilter); |
||||
|
alarmCondition.setCondition(condition); |
||||
|
alarmRule.setCondition(alarmCondition); |
||||
|
deviceProfileAlarm.setClearRule(alarmRule); |
||||
|
TreeMap<AlarmSeverity, AlarmRule> createRules = new TreeMap<>(); |
||||
|
createRules.put(AlarmSeverity.CRITICAL, alarmRule); |
||||
|
deviceProfileAlarm.setCreateRules(createRules); |
||||
|
alarms.add(deviceProfileAlarm); |
||||
|
profileData.setAlarms(alarms); |
||||
|
profileData.setProvisionConfiguration(new AllowCreateNewDevicesDeviceProfileProvisionConfiguration("123")); |
||||
|
} |
||||
|
|
||||
|
private void verifyEdgeConnectionAndInitialData() throws Exception { |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
|
||||
|
validateEdgeConfiguration(); |
||||
|
|
||||
|
// 5 messages
|
||||
|
// - 2 from device profile fetcher (default and thermostat)
|
||||
|
// - 1 from device fetcher
|
||||
|
// - 1 from device profile controller (thermostat)
|
||||
|
// - 1 from device controller (thermostat)
|
||||
|
validateDeviceProfiles(); |
||||
|
|
||||
|
// 2 messages - 1 from device fetcher and 1 from device controller
|
||||
|
validateDevices(); |
||||
|
|
||||
|
// 2 messages - 1 from asset fetcher and 1 from asset controller
|
||||
|
validateAssets(); |
||||
|
|
||||
|
// 2 messages - 1 from rule chain fetcher and 1 from rule chain controller
|
||||
|
UUID ruleChainUUID = validateRuleChains(); |
||||
|
|
||||
|
// 1 from request message
|
||||
|
validateRuleChainMetadataUpdates(ruleChainUUID); |
||||
|
|
||||
|
// 4 messages - 4 messages from fetcher - 2 from system level ('mail', 'mailTemplates') and 2 from admin level ('mail', 'mailTemplates')
|
||||
|
validateAdminSettings(); |
||||
|
|
||||
|
// 3 messages
|
||||
|
// - 1 message from asset profile fetcher
|
||||
|
// - 1 message from asset fetcher
|
||||
|
// - 1 message from asset controller
|
||||
|
validateAssetProfiles(); |
||||
|
|
||||
|
// 1 message from queue fetcher
|
||||
|
validateQueues(); |
||||
|
|
||||
|
// 1 message from user fetcher
|
||||
|
validateUsers(); |
||||
|
} |
||||
|
|
||||
|
private void validateEdgeConfiguration() throws Exception { |
||||
|
EdgeConfiguration configuration = edgeImitator.getConfiguration(); |
||||
|
Assert.assertNotNull(configuration); |
||||
|
testAutoGeneratedCodeByProtobuf(configuration); |
||||
|
} |
||||
|
|
||||
|
private void validateDeviceProfiles() throws Exception { |
||||
|
List<DeviceProfileUpdateMsg> deviceProfileUpdateMsgList = edgeImitator.findAllMessagesByType(DeviceProfileUpdateMsg.class); |
||||
|
// default msg
|
||||
|
// thermostat msg from fetcher
|
||||
|
// thermostat msg from device fetcher
|
||||
|
// thermostat msg from controller
|
||||
|
// thermostat msg from creation of device
|
||||
|
Assert.assertEquals(5, deviceProfileUpdateMsgList.size()); |
||||
|
Optional<DeviceProfileUpdateMsg> thermostatProfileUpdateMsgOpt = |
||||
|
deviceProfileUpdateMsgList.stream().filter(dfum -> THERMOSTAT_DEVICE_PROFILE_NAME.equals(dfum.getName())).findAny(); |
||||
|
Assert.assertTrue(thermostatProfileUpdateMsgOpt.isPresent()); |
||||
|
DeviceProfileUpdateMsg thermostatProfileUpdateMsg = thermostatProfileUpdateMsgOpt.get(); |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, thermostatProfileUpdateMsg.getMsgType()); |
||||
|
UUID deviceProfileUUID = new UUID(thermostatProfileUpdateMsg.getIdMSB(), thermostatProfileUpdateMsg.getIdLSB()); |
||||
|
DeviceProfile deviceProfile = doGet("/api/deviceProfile/" + deviceProfileUUID, DeviceProfile.class); |
||||
|
Assert.assertNotNull(deviceProfile); |
||||
|
Assert.assertNotNull(deviceProfile.getProfileData()); |
||||
|
Assert.assertNotNull(deviceProfile.getProfileData().getAlarms()); |
||||
|
Assert.assertNotNull(deviceProfile.getProfileData().getAlarms().get(0).getClearRule()); |
||||
|
|
||||
|
testAutoGeneratedCodeByProtobuf(thermostatProfileUpdateMsg); |
||||
|
} |
||||
|
|
||||
|
private void validateDevices() throws Exception { |
||||
|
List<DeviceUpdateMsg> deviceUpdateMsgs = edgeImitator.findAllMessagesByType(DeviceUpdateMsg.class); |
||||
|
Assert.assertEquals(2, deviceUpdateMsgs.size()); |
||||
|
validateDevice(deviceUpdateMsgs.get(0)); |
||||
|
validateDevice(deviceUpdateMsgs.get(1)); |
||||
|
} |
||||
|
|
||||
|
private void validateDevice(DeviceUpdateMsg deviceUpdateMsg) throws Exception { |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceUpdateMsg.getMsgType()); |
||||
|
UUID deviceUUID = new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB()); |
||||
|
Device device = doGet("/api/device/" + deviceUUID, Device.class); |
||||
|
Assert.assertNotNull(device); |
||||
|
List<Device> edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getUuidId() + "/devices?", |
||||
|
new TypeReference<PageData<Device>>() {}, new PageLink(100)).getData(); |
||||
|
Assert.assertTrue(edgeDevices.contains(device)); |
||||
|
|
||||
|
testAutoGeneratedCodeByProtobuf(deviceUpdateMsg); |
||||
|
} |
||||
|
|
||||
|
private void validateAssets() throws Exception { |
||||
|
List<AssetUpdateMsg> assetUpdateMsgs = edgeImitator.findAllMessagesByType(AssetUpdateMsg.class); |
||||
|
Assert.assertEquals(2, assetUpdateMsgs.size()); |
||||
|
validateAsset(assetUpdateMsgs.get(0)); |
||||
|
validateAsset(assetUpdateMsgs.get(1)); |
||||
|
} |
||||
|
|
||||
|
private void validateAsset(AssetUpdateMsg assetUpdateMsg) throws Exception { |
||||
|
Assert.assertNotNull(assetUpdateMsg); |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, assetUpdateMsg.getMsgType()); |
||||
|
UUID assetUUID = new UUID(assetUpdateMsg.getIdMSB(), assetUpdateMsg.getIdLSB()); |
||||
|
Asset asset = doGet("/api/asset/" + assetUUID, Asset.class); |
||||
|
Assert.assertNotNull(asset); |
||||
|
List<Asset> edgeAssets = doGetTypedWithPageLink("/api/edge/" + edge.getUuidId() + "/assets?", |
||||
|
new TypeReference<PageData<Asset>>() {}, new PageLink(100)).getData(); |
||||
|
Assert.assertTrue(edgeAssets.contains(asset)); |
||||
|
|
||||
|
testAutoGeneratedCodeByProtobuf(assetUpdateMsg); |
||||
|
} |
||||
|
|
||||
|
private UUID validateRuleChains() throws Exception { |
||||
|
List<RuleChainUpdateMsg> ruleChainUpdateMsgs = edgeImitator.findAllMessagesByType(RuleChainUpdateMsg.class); |
||||
|
Assert.assertEquals(2, ruleChainUpdateMsgs.size()); |
||||
|
RuleChainUpdateMsg ruleChainCreateMsg = ruleChainUpdateMsgs.get(0); |
||||
|
RuleChainUpdateMsg ruleChainUpdateMsg = ruleChainUpdateMsgs.get(1); |
||||
|
validateRuleChain(ruleChainCreateMsg, UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); |
||||
|
validateRuleChain(ruleChainUpdateMsg, UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE); |
||||
|
return new UUID(ruleChainUpdateMsg.getIdMSB(), ruleChainUpdateMsg.getIdLSB()); |
||||
|
} |
||||
|
|
||||
|
private void validateRuleChain(RuleChainUpdateMsg ruleChainUpdateMsg, UpdateMsgType expectedMsgType) throws Exception { |
||||
|
Assert.assertEquals(expectedMsgType, ruleChainUpdateMsg.getMsgType()); |
||||
|
UUID ruleChainUUID = new UUID(ruleChainUpdateMsg.getIdMSB(), ruleChainUpdateMsg.getIdLSB()); |
||||
|
RuleChain ruleChain = doGet("/api/ruleChain/" + ruleChainUUID, RuleChain.class); |
||||
|
Assert.assertNotNull(ruleChain); |
||||
|
List<RuleChain> edgeRuleChains = doGetTypedWithPageLink("/api/edge/" + edge.getUuidId() + "/ruleChains?", |
||||
|
new TypeReference<PageData<RuleChain>>() {}, new PageLink(100)).getData(); |
||||
|
Assert.assertTrue(edgeRuleChains.contains(ruleChain)); |
||||
|
testAutoGeneratedCodeByProtobuf(ruleChainUpdateMsg); |
||||
|
} |
||||
|
|
||||
|
private void validateRuleChainMetadataUpdates(UUID expectedRuleChainUUID) { |
||||
|
Optional<RuleChainMetadataUpdateMsg> ruleChainMetadataUpdateOpt = edgeImitator.findMessageByType(RuleChainMetadataUpdateMsg.class); |
||||
|
Assert.assertTrue(ruleChainMetadataUpdateOpt.isPresent()); |
||||
|
RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = ruleChainMetadataUpdateOpt.get(); |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, ruleChainMetadataUpdateMsg.getMsgType()); |
||||
|
UUID ruleChainUUID = new UUID(ruleChainMetadataUpdateMsg.getRuleChainIdMSB(), ruleChainMetadataUpdateMsg.getRuleChainIdLSB()); |
||||
|
Assert.assertEquals(expectedRuleChainUUID, ruleChainUUID); |
||||
|
} |
||||
|
|
||||
|
private void validateAdminSettings() throws JsonProcessingException { |
||||
|
List<AdminSettingsUpdateMsg> adminSettingsUpdateMsgs = edgeImitator.findAllMessagesByType(AdminSettingsUpdateMsg.class); |
||||
|
Assert.assertEquals(4, adminSettingsUpdateMsgs.size()); |
||||
|
|
||||
|
for (AdminSettingsUpdateMsg adminSettingsUpdateMsg : adminSettingsUpdateMsgs) { |
||||
|
if (adminSettingsUpdateMsg.getKey().equals("mail")) { |
||||
|
validateMailAdminSettings(adminSettingsUpdateMsg); |
||||
|
} |
||||
|
if (adminSettingsUpdateMsg.getKey().equals("mailTemplates")) { |
||||
|
validateMailTemplatesAdminSettings(adminSettingsUpdateMsg); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void validateMailAdminSettings(AdminSettingsUpdateMsg adminSettingsUpdateMsg) throws JsonProcessingException { |
||||
|
JsonNode jsonNode = mapper.readTree(adminSettingsUpdateMsg.getJsonValue()); |
||||
|
Assert.assertNotNull(jsonNode.get("mailFrom")); |
||||
|
Assert.assertNotNull(jsonNode.get("smtpProtocol")); |
||||
|
Assert.assertNotNull(jsonNode.get("smtpHost")); |
||||
|
Assert.assertNotNull(jsonNode.get("smtpPort")); |
||||
|
Assert.assertNotNull(jsonNode.get("timeout")); |
||||
|
} |
||||
|
|
||||
|
private void validateMailTemplatesAdminSettings(AdminSettingsUpdateMsg adminSettingsUpdateMsg) throws JsonProcessingException { |
||||
|
JsonNode jsonNode = mapper.readTree(adminSettingsUpdateMsg.getJsonValue()); |
||||
|
Assert.assertNotNull(jsonNode.get("accountActivated")); |
||||
|
Assert.assertNotNull(jsonNode.get("accountLockout")); |
||||
|
Assert.assertNotNull(jsonNode.get("activation")); |
||||
|
Assert.assertNotNull(jsonNode.get("passwordWasReset")); |
||||
|
Assert.assertNotNull(jsonNode.get("resetPassword")); |
||||
|
Assert.assertNotNull(jsonNode.get("test")); |
||||
|
} |
||||
|
|
||||
|
private void validateAssetProfiles() throws Exception { |
||||
|
List<AssetProfileUpdateMsg> assetProfileUpdateMsgs = edgeImitator.findAllMessagesByType(AssetProfileUpdateMsg.class); |
||||
|
Assert.assertEquals(3, assetProfileUpdateMsgs.size()); |
||||
|
AssetProfileUpdateMsg assetProfileUpdateMsg = assetProfileUpdateMsgs.get(0); |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, assetProfileUpdateMsg.getMsgType()); |
||||
|
UUID assetProfileUUID = new UUID(assetProfileUpdateMsg.getIdMSB(), assetProfileUpdateMsg.getIdLSB()); |
||||
|
AssetProfile assetProfile = doGet("/api/assetProfile/" + assetProfileUUID, AssetProfile.class); |
||||
|
Assert.assertNotNull(assetProfile); |
||||
|
Assert.assertEquals("default", assetProfile.getName()); |
||||
|
Assert.assertTrue(assetProfile.isDefault()); |
||||
|
testAutoGeneratedCodeByProtobuf(assetProfileUpdateMsg); |
||||
|
} |
||||
|
|
||||
|
private void validateQueues() throws Exception { |
||||
|
Optional<QueueUpdateMsg> queueUpdateMsgOpt = edgeImitator.findMessageByType(QueueUpdateMsg.class); |
||||
|
Assert.assertTrue(queueUpdateMsgOpt.isPresent()); |
||||
|
QueueUpdateMsg queueUpdateMsg = queueUpdateMsgOpt.get(); |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, queueUpdateMsg.getMsgType()); |
||||
|
UUID queueUUID = new UUID(queueUpdateMsg.getIdMSB(), queueUpdateMsg.getIdLSB()); |
||||
|
Queue queue = doGet("/api/queues/" + queueUUID, Queue.class); |
||||
|
Assert.assertNotNull(queue); |
||||
|
Assert.assertEquals("Main", queueUpdateMsg.getName()); |
||||
|
Assert.assertEquals("tb_rule_engine.main", queueUpdateMsg.getTopic()); |
||||
|
Assert.assertEquals(10, queueUpdateMsg.getPartitions()); |
||||
|
Assert.assertEquals(25, queueUpdateMsg.getPollInterval()); |
||||
|
testAutoGeneratedCodeByProtobuf(queueUpdateMsg); |
||||
|
} |
||||
|
|
||||
|
private void validateUsers() throws Exception { |
||||
|
Optional<UserUpdateMsg> userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class); |
||||
|
Assert.assertTrue(userUpdateMsgOpt.isPresent()); |
||||
|
UserUpdateMsg userUpdateMsg = userUpdateMsgOpt.get(); |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, userUpdateMsg.getMsgType()); |
||||
|
UUID userUUID = new UUID(userUpdateMsg.getIdMSB(), userUpdateMsg.getIdLSB()); |
||||
|
User user = doGet("/api/user/" + userUUID, User.class); |
||||
|
Assert.assertNotNull(user); |
||||
|
Assert.assertEquals("tenant2@thingsboard.org", userUpdateMsg.getEmail()); |
||||
|
testAutoGeneratedCodeByProtobuf(userUpdateMsg); |
||||
|
} |
||||
|
|
||||
|
protected Device saveDeviceOnCloudAndVerifyDeliveryToEdge() throws Exception { |
||||
|
// create ota package
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
OtaPackageInfo firmwareOtaPackageInfo = saveOtaPackageInfo(thermostatDeviceProfile.getId()); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
|
||||
|
// create device and assign to edge
|
||||
|
Device savedDevice = saveDevice(StringUtils.randomAlphanumeric(15), thermostatDeviceProfile.getName()); |
||||
|
edgeImitator.expectMessageAmount(2); // device and device profile messages
|
||||
|
doPost("/api/edge/" + edge.getUuidId() |
||||
|
+ "/device/" + savedDevice.getUuidId(), Device.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
Optional<DeviceUpdateMsg> deviceUpdateMsgOpt = edgeImitator.findMessageByType(DeviceUpdateMsg.class); |
||||
|
Assert.assertTrue(deviceUpdateMsgOpt.isPresent()); |
||||
|
DeviceUpdateMsg deviceUpdateMsg = deviceUpdateMsgOpt.get(); |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedDevice.getUuidId().getMostSignificantBits(), deviceUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(savedDevice.getUuidId().getLeastSignificantBits(), deviceUpdateMsg.getIdLSB()); |
||||
|
|
||||
|
Optional<DeviceProfileUpdateMsg> deviceProfileUpdateMsgOpt = edgeImitator.findMessageByType(DeviceProfileUpdateMsg.class); |
||||
|
Assert.assertTrue(deviceProfileUpdateMsgOpt.isPresent()); |
||||
|
DeviceProfileUpdateMsg deviceProfileUpdateMsg = deviceProfileUpdateMsgOpt.get(); |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(thermostatDeviceProfile.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(thermostatDeviceProfile.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getIdLSB()); |
||||
|
|
||||
|
// update device
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
savedDevice.setFirmwareId(firmwareOtaPackageInfo.getId()); |
||||
|
|
||||
|
DeviceData deviceData = new DeviceData(); |
||||
|
deviceData.setConfiguration(new DefaultDeviceConfiguration()); |
||||
|
MqttDeviceTransportConfiguration transportConfiguration = new MqttDeviceTransportConfiguration(); |
||||
|
transportConfiguration.getProperties().put("topic", "tb_rule_engine.thermostat"); |
||||
|
deviceData.setTransportConfiguration(transportConfiguration); |
||||
|
savedDevice.setDeviceData(deviceData); |
||||
|
|
||||
|
savedDevice = doPost("/api/device", savedDevice, Device.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
AbstractMessage latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof DeviceUpdateMsg); |
||||
|
deviceUpdateMsg = (DeviceUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, deviceUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedDevice.getUuidId().getMostSignificantBits(), deviceUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(savedDevice.getUuidId().getLeastSignificantBits(), deviceUpdateMsg.getIdLSB()); |
||||
|
Assert.assertEquals(savedDevice.getName(), deviceUpdateMsg.getName()); |
||||
|
Assert.assertEquals(savedDevice.getType(), deviceUpdateMsg.getType()); |
||||
|
Assert.assertEquals(firmwareOtaPackageInfo.getUuidId().getMostSignificantBits(), deviceUpdateMsg.getFirmwareIdMSB()); |
||||
|
Assert.assertEquals(firmwareOtaPackageInfo.getUuidId().getLeastSignificantBits(), deviceUpdateMsg.getFirmwareIdLSB()); |
||||
|
Optional<DeviceData> deviceDataOpt = |
||||
|
dataDecodingEncodingService.decode(deviceUpdateMsg.getDeviceDataBytes().toByteArray()); |
||||
|
Assert.assertTrue(deviceDataOpt.isPresent()); |
||||
|
deviceData = deviceDataOpt.get(); |
||||
|
Assert.assertTrue(deviceData.getTransportConfiguration() instanceof MqttDeviceTransportConfiguration); |
||||
|
MqttDeviceTransportConfiguration mqttDeviceTransportConfiguration = |
||||
|
(MqttDeviceTransportConfiguration) deviceData.getTransportConfiguration(); |
||||
|
Assert.assertEquals("tb_rule_engine.thermostat", mqttDeviceTransportConfiguration.getProperties().get("topic")); |
||||
|
return savedDevice; |
||||
|
} |
||||
|
|
||||
|
protected Device findDeviceByName(String deviceName) throws Exception { |
||||
|
List<Device> edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getUuidId() + "/devices?", |
||||
|
new TypeReference<PageData<Device>>() { |
||||
|
}, new PageLink(100)).getData(); |
||||
|
Optional<Device> foundDevice = edgeDevices.stream().filter(d -> d.getName().equals(deviceName)).findAny(); |
||||
|
Assert.assertTrue(foundDevice.isPresent()); |
||||
|
Device device = foundDevice.get(); |
||||
|
Assert.assertEquals(deviceName, device.getName()); |
||||
|
return device; |
||||
|
} |
||||
|
|
||||
|
protected Asset findAssetByName(String assetName) throws Exception { |
||||
|
List<Asset> edgeAssets = doGetTypedWithPageLink("/api/edge/" + edge.getUuidId() + "/assets?", |
||||
|
new TypeReference<PageData<Asset>>() { |
||||
|
}, new PageLink(100)).getData(); |
||||
|
|
||||
|
Assert.assertEquals(1, edgeAssets.size()); |
||||
|
Asset asset = edgeAssets.get(0); |
||||
|
Assert.assertEquals(assetName, asset.getName()); |
||||
|
return asset; |
||||
|
} |
||||
|
|
||||
|
protected Device saveDevice(String deviceName, String type) { |
||||
|
Device device = new Device(); |
||||
|
device.setName(deviceName); |
||||
|
device.setType(type); |
||||
|
return doPost("/api/device", device, Device.class); |
||||
|
} |
||||
|
|
||||
|
protected Asset saveAsset(String assetName) { |
||||
|
Asset asset = new Asset(); |
||||
|
asset.setName(assetName); |
||||
|
return doPost("/api/asset", asset, Asset.class); |
||||
|
} |
||||
|
|
||||
|
protected OtaPackageInfo saveOtaPackageInfo(DeviceProfileId deviceProfileId) { |
||||
|
SaveOtaPackageInfoRequest firmwareInfo = new SaveOtaPackageInfoRequest(); |
||||
|
firmwareInfo.setDeviceProfileId(deviceProfileId); |
||||
|
firmwareInfo.setType(FIRMWARE); |
||||
|
firmwareInfo.setTitle("Firmware Edge " + StringUtils.randomAlphanumeric(3)); |
||||
|
firmwareInfo.setVersion("v1.0"); |
||||
|
firmwareInfo.setTag("My firmware #1 v1.0"); |
||||
|
firmwareInfo.setUsesUrl(true); |
||||
|
firmwareInfo.setUrl("http://localhost:8080/v1/package"); |
||||
|
firmwareInfo.setAdditionalInfo(JacksonUtil.newObjectNode()); |
||||
|
firmwareInfo.setChecksumAlgorithm(ChecksumAlgorithm.SHA256); |
||||
|
return doPost("/api/otaPackage", firmwareInfo, OtaPackageInfo.class); |
||||
|
} |
||||
|
|
||||
|
protected EdgeEvent constructEdgeEvent(TenantId tenantId, EdgeId edgeId, EdgeEventActionType edgeEventAction, |
||||
|
UUID entityId, EdgeEventType edgeEventType, JsonNode entityBody) { |
||||
|
EdgeEvent edgeEvent = new EdgeEvent(); |
||||
|
edgeEvent.setEdgeId(edgeId); |
||||
|
edgeEvent.setTenantId(tenantId); |
||||
|
edgeEvent.setAction(edgeEventAction); |
||||
|
edgeEvent.setEntityId(entityId); |
||||
|
edgeEvent.setType(edgeEventType); |
||||
|
edgeEvent.setBody(entityBody); |
||||
|
return edgeEvent; |
||||
|
} |
||||
|
|
||||
|
protected void testAutoGeneratedCodeByProtobuf(MessageLite.Builder builder) throws InvalidProtocolBufferException { |
||||
|
MessageLite source = builder.build(); |
||||
|
|
||||
|
testAutoGeneratedCodeByProtobuf(source); |
||||
|
|
||||
|
MessageLite target = source.getParserForType().parseFrom(source.toByteArray()); |
||||
|
builder.clear().mergeFrom(target); |
||||
|
} |
||||
|
|
||||
|
protected void testAutoGeneratedCodeByProtobuf(MessageLite source) throws InvalidProtocolBufferException { |
||||
|
MessageLite target = source.getParserForType().parseFrom(source.toByteArray()); |
||||
|
Assert.assertEquals(source, target); |
||||
|
Assert.assertEquals(source.hashCode(), target.hashCode()); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
} |
||||
@ -0,0 +1,136 @@ |
|||||
|
/** |
||||
|
* 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.edge; |
||||
|
|
||||
|
import com.fasterxml.jackson.core.type.TypeReference; |
||||
|
import com.google.protobuf.AbstractMessage; |
||||
|
import org.junit.Assert; |
||||
|
import org.junit.Test; |
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
import org.thingsboard.server.common.data.EntityType; |
||||
|
import org.thingsboard.server.common.data.alarm.Alarm; |
||||
|
import org.thingsboard.server.common.data.alarm.AlarmInfo; |
||||
|
import org.thingsboard.server.common.data.alarm.AlarmSeverity; |
||||
|
import org.thingsboard.server.common.data.alarm.AlarmStatus; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; |
||||
|
import org.thingsboard.server.gen.edge.v1.UplinkMsg; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.Optional; |
||||
|
|
||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
|
||||
|
abstract public class BaseAlarmEdgeTest extends AbstractEdgeTest { |
||||
|
|
||||
|
@Test |
||||
|
public void testSendAlarmToCloud() throws Exception { |
||||
|
Device device = saveDeviceOnCloudAndVerifyDeliveryToEdge(); |
||||
|
|
||||
|
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); |
||||
|
AlarmUpdateMsg.Builder alarmUpdateMgBuilder = AlarmUpdateMsg.newBuilder(); |
||||
|
alarmUpdateMgBuilder.setName("alarm from edge"); |
||||
|
alarmUpdateMgBuilder.setStatus(AlarmStatus.ACTIVE_UNACK.name()); |
||||
|
alarmUpdateMgBuilder.setSeverity(AlarmSeverity.CRITICAL.name()); |
||||
|
alarmUpdateMgBuilder.setOriginatorName(device.getName()); |
||||
|
alarmUpdateMgBuilder.setOriginatorType(EntityType.DEVICE.name()); |
||||
|
testAutoGeneratedCodeByProtobuf(alarmUpdateMgBuilder); |
||||
|
uplinkMsgBuilder.addAlarmUpdateMsg(alarmUpdateMgBuilder.build()); |
||||
|
|
||||
|
testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder); |
||||
|
|
||||
|
edgeImitator.expectResponsesAmount(1); |
||||
|
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); |
||||
|
Assert.assertTrue(edgeImitator.waitForResponses()); |
||||
|
|
||||
|
List<AlarmInfo> alarms = doGetTypedWithPageLink("/api/alarm/{entityType}/{entityId}?", |
||||
|
new TypeReference<PageData<AlarmInfo>>() {}, |
||||
|
new PageLink(100), device.getId().getEntityType().name(), device.getUuidId()) |
||||
|
.getData(); |
||||
|
Optional<AlarmInfo> foundAlarm = alarms.stream().filter(alarm -> alarm.getType().equals("alarm from edge")).findAny(); |
||||
|
Assert.assertTrue(foundAlarm.isPresent()); |
||||
|
AlarmInfo alarmInfo = foundAlarm.get(); |
||||
|
Assert.assertEquals(device.getId(), alarmInfo.getOriginator()); |
||||
|
Assert.assertEquals(AlarmStatus.ACTIVE_UNACK, alarmInfo.getStatus()); |
||||
|
Assert.assertEquals(AlarmSeverity.CRITICAL, alarmInfo.getSeverity()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testAlarms() throws Exception { |
||||
|
// create alarm
|
||||
|
Device device = findDeviceByName("Edge Device 1"); |
||||
|
Alarm alarm = new Alarm(); |
||||
|
alarm.setOriginator(device.getId()); |
||||
|
alarm.setStatus(AlarmStatus.ACTIVE_UNACK); |
||||
|
alarm.setType("alarm"); |
||||
|
alarm.setSeverity(AlarmSeverity.CRITICAL); |
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
Alarm savedAlarm = doPost("/api/alarm", alarm, Alarm.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
AbstractMessage latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof AlarmUpdateMsg); |
||||
|
AlarmUpdateMsg alarmUpdateMsg = (AlarmUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, alarmUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedAlarm.getType(), alarmUpdateMsg.getType()); |
||||
|
Assert.assertEquals(savedAlarm.getName(), alarmUpdateMsg.getName()); |
||||
|
Assert.assertEquals(device.getName(), alarmUpdateMsg.getOriginatorName()); |
||||
|
Assert.assertEquals(savedAlarm.getStatus().name(), alarmUpdateMsg.getStatus()); |
||||
|
Assert.assertEquals(savedAlarm.getSeverity().name(), alarmUpdateMsg.getSeverity()); |
||||
|
|
||||
|
// ack alarm
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
doPost("/api/alarm/" + savedAlarm.getUuidId() + "/ack"); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof AlarmUpdateMsg); |
||||
|
alarmUpdateMsg = (AlarmUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ALARM_ACK_RPC_MESSAGE, alarmUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedAlarm.getType(), alarmUpdateMsg.getType()); |
||||
|
Assert.assertEquals(savedAlarm.getName(), alarmUpdateMsg.getName()); |
||||
|
Assert.assertEquals(device.getName(), alarmUpdateMsg.getOriginatorName()); |
||||
|
Assert.assertEquals(AlarmStatus.ACTIVE_ACK.name(), alarmUpdateMsg.getStatus()); |
||||
|
|
||||
|
// clear alarm
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
doPost("/api/alarm/" + savedAlarm.getUuidId() + "/clear"); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof AlarmUpdateMsg); |
||||
|
alarmUpdateMsg = (AlarmUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ALARM_CLEAR_RPC_MESSAGE, alarmUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedAlarm.getType(), alarmUpdateMsg.getType()); |
||||
|
Assert.assertEquals(savedAlarm.getName(), alarmUpdateMsg.getName()); |
||||
|
Assert.assertEquals(device.getName(), alarmUpdateMsg.getOriginatorName()); |
||||
|
Assert.assertEquals(AlarmStatus.CLEARED_ACK.name(), alarmUpdateMsg.getStatus()); |
||||
|
|
||||
|
// delete alarm
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
doDelete("/api/alarm/" + savedAlarm.getUuidId()) |
||||
|
.andExpect(status().isOk()); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof AlarmUpdateMsg); |
||||
|
alarmUpdateMsg = (AlarmUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, alarmUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedAlarm.getType(), alarmUpdateMsg.getType()); |
||||
|
Assert.assertEquals(savedAlarm.getName(), alarmUpdateMsg.getName()); |
||||
|
Assert.assertEquals(device.getName(), alarmUpdateMsg.getOriginatorName()); |
||||
|
Assert.assertEquals(AlarmStatus.CLEARED_ACK.name(), alarmUpdateMsg.getStatus()); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,157 @@ |
|||||
|
/** |
||||
|
* 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.edge; |
||||
|
|
||||
|
import com.google.protobuf.AbstractMessage; |
||||
|
import org.junit.Assert; |
||||
|
import org.junit.Test; |
||||
|
import org.thingsboard.server.common.data.Customer; |
||||
|
import org.thingsboard.server.common.data.asset.Asset; |
||||
|
import org.thingsboard.server.common.data.edge.Edge; |
||||
|
import org.thingsboard.server.common.data.id.CustomerId; |
||||
|
import org.thingsboard.server.common.data.id.EntityId; |
||||
|
import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; |
||||
|
|
||||
|
import java.util.Optional; |
||||
|
import java.util.UUID; |
||||
|
|
||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
|
||||
|
abstract public class BaseAssetEdgeTest extends AbstractEdgeTest { |
||||
|
|
||||
|
@Test |
||||
|
public void testAssets() throws Exception { |
||||
|
// create asset and assign to edge
|
||||
|
edgeImitator.expectMessageAmount(2); |
||||
|
Asset savedAsset = saveAsset("Edge Asset 2"); |
||||
|
doPost("/api/edge/" + edge.getUuidId() |
||||
|
+ "/asset/" + savedAsset.getUuidId(), Asset.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
Optional<AssetUpdateMsg> assetUpdateMsgOpt = edgeImitator.findMessageByType(AssetUpdateMsg.class); |
||||
|
Assert.assertTrue(assetUpdateMsgOpt.isPresent()); |
||||
|
AssetUpdateMsg assetUpdateMsg = assetUpdateMsgOpt.get(); |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, assetUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedAsset.getUuidId().getMostSignificantBits(), assetUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(savedAsset.getUuidId().getLeastSignificantBits(), assetUpdateMsg.getIdLSB()); |
||||
|
Assert.assertEquals(savedAsset.getName(), assetUpdateMsg.getName()); |
||||
|
Assert.assertEquals(savedAsset.getType(), assetUpdateMsg.getType()); |
||||
|
Optional<AssetProfileUpdateMsg> assetProfileUpdateMsgOpt = edgeImitator.findMessageByType(AssetProfileUpdateMsg.class); |
||||
|
Assert.assertTrue(assetProfileUpdateMsgOpt.isPresent()); |
||||
|
AssetProfileUpdateMsg assetProfileUpdateMsg = assetProfileUpdateMsgOpt.get(); |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, assetProfileUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedAsset.getAssetProfileId().getId().getMostSignificantBits(), assetProfileUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(savedAsset.getAssetProfileId().getId().getLeastSignificantBits(), assetProfileUpdateMsg.getIdLSB()); |
||||
|
|
||||
|
// update asset
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
savedAsset.setName("Edge Asset 2 Updated"); |
||||
|
savedAsset = doPost("/api/asset", savedAsset, Asset.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
AbstractMessage latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof AssetUpdateMsg); |
||||
|
assetUpdateMsg = (AssetUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, assetUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedAsset.getName(), assetUpdateMsg.getName()); |
||||
|
|
||||
|
// unassign asset from edge
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
doDelete("/api/edge/" + edge.getUuidId() |
||||
|
+ "/asset/" + savedAsset.getUuidId(), Asset.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof AssetUpdateMsg); |
||||
|
assetUpdateMsg = (AssetUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, assetUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedAsset.getUuidId().getMostSignificantBits(), assetUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(savedAsset.getUuidId().getLeastSignificantBits(), assetUpdateMsg.getIdLSB()); |
||||
|
|
||||
|
// delete asset - no messages expected
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
doDelete("/api/asset/" + savedAsset.getUuidId()) |
||||
|
.andExpect(status().isOk()); |
||||
|
Assert.assertFalse(edgeImitator.waitForMessages(1)); |
||||
|
|
||||
|
// create asset #2 and assign to edge
|
||||
|
edgeImitator.expectMessageAmount(2); |
||||
|
savedAsset = saveAsset("Edge Asset 3"); |
||||
|
doPost("/api/edge/" + edge.getUuidId() |
||||
|
+ "/asset/" + savedAsset.getUuidId(), Asset.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
latestMessage = edgeImitator.getLatestMessage(); |
||||
|
assetUpdateMsgOpt = edgeImitator.findMessageByType(AssetUpdateMsg.class); |
||||
|
Assert.assertTrue(assetUpdateMsgOpt.isPresent()); |
||||
|
assetUpdateMsg = assetUpdateMsgOpt.get(); |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, assetUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedAsset.getUuidId().getMostSignificantBits(), assetUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(savedAsset.getUuidId().getLeastSignificantBits(), assetUpdateMsg.getIdLSB()); |
||||
|
Assert.assertEquals(savedAsset.getName(), assetUpdateMsg.getName()); |
||||
|
Assert.assertEquals(savedAsset.getType(), assetUpdateMsg.getType()); |
||||
|
assetProfileUpdateMsgOpt = edgeImitator.findMessageByType(AssetProfileUpdateMsg.class); |
||||
|
Assert.assertTrue(assetProfileUpdateMsgOpt.isPresent()); |
||||
|
assetProfileUpdateMsg = assetProfileUpdateMsgOpt.get(); |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, assetProfileUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedAsset.getAssetProfileId().getId().getMostSignificantBits(), assetProfileUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(savedAsset.getAssetProfileId().getId().getLeastSignificantBits(), assetProfileUpdateMsg.getIdLSB()); |
||||
|
|
||||
|
// assign asset #2 to customer
|
||||
|
Customer customer = new Customer(); |
||||
|
customer.setTitle("Edge Customer"); |
||||
|
Customer savedCustomer = doPost("/api/customer", customer, Customer.class); |
||||
|
edgeImitator.expectMessageAmount(2); |
||||
|
doPost("/api/customer/" + savedCustomer.getUuidId() |
||||
|
+ "/edge/" + edge.getUuidId(), Edge.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
doPost("/api/customer/" + savedCustomer.getUuidId() |
||||
|
+ "/asset/" + savedAsset.getUuidId(), Asset.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof AssetUpdateMsg); |
||||
|
assetUpdateMsg = (AssetUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, assetUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedCustomer.getUuidId().getMostSignificantBits(), assetUpdateMsg.getCustomerIdMSB()); |
||||
|
Assert.assertEquals(savedCustomer.getUuidId().getLeastSignificantBits(), assetUpdateMsg.getCustomerIdLSB()); |
||||
|
|
||||
|
// unassign asset #2 from customer
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
doDelete("/api/customer/asset/" + savedAsset.getUuidId(), Asset.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof AssetUpdateMsg); |
||||
|
assetUpdateMsg = (AssetUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, assetUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals( |
||||
|
new CustomerId(EntityId.NULL_UUID), |
||||
|
new CustomerId(new UUID(assetUpdateMsg.getCustomerIdMSB(), assetUpdateMsg.getCustomerIdLSB()))); |
||||
|
|
||||
|
// delete asset #2 - messages expected
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
doDelete("/api/asset/" + savedAsset.getUuidId()) |
||||
|
.andExpect(status().isOk()); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof AssetUpdateMsg); |
||||
|
assetUpdateMsg = (AssetUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, assetUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedAsset.getUuidId().getMostSignificantBits(), assetUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(savedAsset.getUuidId().getLeastSignificantBits(), assetUpdateMsg.getIdLSB()); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
@ -0,0 +1,70 @@ |
|||||
|
/** |
||||
|
* 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.edge; |
||||
|
|
||||
|
import com.google.protobuf.AbstractMessage; |
||||
|
import com.google.protobuf.ByteString; |
||||
|
import org.junit.Assert; |
||||
|
import org.junit.Test; |
||||
|
import org.thingsboard.server.common.data.asset.AssetProfile; |
||||
|
import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; |
||||
|
|
||||
|
import java.nio.charset.StandardCharsets; |
||||
|
|
||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
|
||||
|
abstract public class BaseAssetProfileEdgeTest extends AbstractEdgeTest { |
||||
|
|
||||
|
@Test |
||||
|
public void testAssetProfiles() throws Exception { |
||||
|
// create asset profile
|
||||
|
AssetProfile assetProfile = this.createAssetProfile("Building"); |
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
assetProfile = doPost("/api/assetProfile", assetProfile, AssetProfile.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
AbstractMessage latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof AssetProfileUpdateMsg); |
||||
|
AssetProfileUpdateMsg assetProfileUpdateMsg = (AssetProfileUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, assetProfileUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(assetProfile.getUuidId().getMostSignificantBits(), assetProfileUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(assetProfile.getUuidId().getLeastSignificantBits(), assetProfileUpdateMsg.getIdLSB()); |
||||
|
Assert.assertEquals("Building", assetProfileUpdateMsg.getName()); |
||||
|
|
||||
|
// update asset profile
|
||||
|
assetProfile.setImage("IMAGE"); |
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
assetProfile = doPost("/api/assetProfile", assetProfile, AssetProfile.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof AssetProfileUpdateMsg); |
||||
|
assetProfileUpdateMsg = (AssetProfileUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, assetProfileUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(ByteString.copyFrom("IMAGE".getBytes(StandardCharsets.UTF_8)), assetProfileUpdateMsg.getImage()); |
||||
|
|
||||
|
// delete profile
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
doDelete("/api/assetProfile/" + assetProfile.getUuidId()) |
||||
|
.andExpect(status().isOk()); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof AssetProfileUpdateMsg); |
||||
|
assetProfileUpdateMsg = (AssetProfileUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, assetProfileUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(assetProfile.getUuidId().getMostSignificantBits(), assetProfileUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(assetProfile.getUuidId().getLeastSignificantBits(), assetProfileUpdateMsg.getIdLSB()); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,87 @@ |
|||||
|
/** |
||||
|
* 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.edge; |
||||
|
|
||||
|
import com.google.protobuf.AbstractMessage; |
||||
|
import org.junit.Assert; |
||||
|
import org.junit.Test; |
||||
|
import org.thingsboard.server.common.data.Customer; |
||||
|
import org.thingsboard.server.common.data.edge.Edge; |
||||
|
import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.EdgeConfiguration; |
||||
|
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; |
||||
|
|
||||
|
import java.util.Optional; |
||||
|
|
||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
|
||||
|
abstract public class BaseCustomerEdgeTest extends AbstractEdgeTest { |
||||
|
|
||||
|
@Test |
||||
|
public void testCreateUpdateDeleteCustomer() throws Exception { |
||||
|
// create customer
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
Customer customer = new Customer(); |
||||
|
customer.setTitle("Edge Customer"); |
||||
|
Customer savedCustomer = doPost("/api/customer", customer, Customer.class); |
||||
|
Assert.assertFalse(edgeImitator.waitForMessages(1)); |
||||
|
|
||||
|
// assign edge to customer
|
||||
|
edgeImitator.expectMessageAmount(2); |
||||
|
doPost("/api/customer/" + savedCustomer.getUuidId() |
||||
|
+ "/edge/" + edge.getUuidId(), Edge.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
Optional<EdgeConfiguration> edgeConfigurationOpt = edgeImitator.findMessageByType(EdgeConfiguration.class); |
||||
|
Assert.assertTrue(edgeConfigurationOpt.isPresent()); |
||||
|
EdgeConfiguration edgeConfiguration = edgeConfigurationOpt.get(); |
||||
|
Assert.assertEquals(savedCustomer.getUuidId().getMostSignificantBits(), edgeConfiguration.getCustomerIdMSB()); |
||||
|
Assert.assertEquals(savedCustomer.getUuidId().getLeastSignificantBits(), edgeConfiguration.getCustomerIdLSB()); |
||||
|
Optional<CustomerUpdateMsg> customerUpdateOpt = edgeImitator.findMessageByType(CustomerUpdateMsg.class); |
||||
|
Assert.assertTrue(customerUpdateOpt.isPresent()); |
||||
|
CustomerUpdateMsg customerUpdateMsg = customerUpdateOpt.get(); |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, customerUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedCustomer.getUuidId().getMostSignificantBits(), customerUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(savedCustomer.getUuidId().getLeastSignificantBits(), customerUpdateMsg.getIdLSB()); |
||||
|
Assert.assertEquals(savedCustomer.getTitle(), customerUpdateMsg.getTitle()); |
||||
|
testAutoGeneratedCodeByProtobuf(customerUpdateMsg); |
||||
|
|
||||
|
// update customer
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
savedCustomer.setTitle("Edge Customer Updated"); |
||||
|
savedCustomer = doPost("/api/customer", savedCustomer, Customer.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
AbstractMessage latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof CustomerUpdateMsg); |
||||
|
customerUpdateMsg = (CustomerUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, customerUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedCustomer.getUuidId().getMostSignificantBits(), customerUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(savedCustomer.getUuidId().getLeastSignificantBits(), customerUpdateMsg.getIdLSB()); |
||||
|
Assert.assertEquals(savedCustomer.getTitle(), customerUpdateMsg.getTitle()); |
||||
|
|
||||
|
// delete customer
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
doDelete("/api/customer/" + savedCustomer.getUuidId()) |
||||
|
.andExpect(status().isOk()); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof CustomerUpdateMsg); |
||||
|
customerUpdateMsg = (CustomerUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, customerUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(customerUpdateMsg.getIdMSB(), savedCustomer.getUuidId().getMostSignificantBits()); |
||||
|
Assert.assertEquals(customerUpdateMsg.getIdLSB(), savedCustomer.getUuidId().getLeastSignificantBits()); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,150 @@ |
|||||
|
/** |
||||
|
* 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.edge; |
||||
|
|
||||
|
import com.fasterxml.jackson.core.type.TypeReference; |
||||
|
import com.google.protobuf.AbstractMessage; |
||||
|
import org.junit.Assert; |
||||
|
import org.junit.Test; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.common.data.Customer; |
||||
|
import org.thingsboard.server.common.data.Dashboard; |
||||
|
import org.thingsboard.server.common.data.ShortCustomerInfo; |
||||
|
import org.thingsboard.server.common.data.edge.Edge; |
||||
|
import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; |
||||
|
|
||||
|
import java.util.Set; |
||||
|
|
||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
|
||||
|
abstract public class BaseDashboardEdgeTest extends AbstractEdgeTest { |
||||
|
|
||||
|
@Test |
||||
|
public void testDashboards() throws Exception { |
||||
|
// create dashboard and assign to edge
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
Dashboard dashboard = new Dashboard(); |
||||
|
dashboard.setTitle("Edge Test Dashboard"); |
||||
|
Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class); |
||||
|
doPost("/api/edge/" + edge.getUuidId() |
||||
|
+ "/dashboard/" + savedDashboard.getUuidId(), Dashboard.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
AbstractMessage latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof DashboardUpdateMsg); |
||||
|
DashboardUpdateMsg dashboardUpdateMsg = (DashboardUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedDashboard.getUuidId().getMostSignificantBits(), dashboardUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(savedDashboard.getUuidId().getLeastSignificantBits(), dashboardUpdateMsg.getIdLSB()); |
||||
|
Assert.assertEquals(savedDashboard.getTitle(), dashboardUpdateMsg.getTitle()); |
||||
|
testAutoGeneratedCodeByProtobuf(dashboardUpdateMsg); |
||||
|
|
||||
|
// update dashboard
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
savedDashboard.setTitle("Updated Edge Test Dashboard"); |
||||
|
savedDashboard = doPost("/api/dashboard", savedDashboard, Dashboard.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof DashboardUpdateMsg); |
||||
|
dashboardUpdateMsg = (DashboardUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedDashboard.getTitle(), dashboardUpdateMsg.getTitle()); |
||||
|
|
||||
|
// unassign dashboard from edge
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
doDelete("/api/edge/" + edge.getUuidId() |
||||
|
+ "/dashboard/" + savedDashboard.getUuidId(), Dashboard.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof DashboardUpdateMsg); |
||||
|
dashboardUpdateMsg = (DashboardUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedDashboard.getUuidId().getMostSignificantBits(), dashboardUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(savedDashboard.getUuidId().getLeastSignificantBits(), dashboardUpdateMsg.getIdLSB()); |
||||
|
|
||||
|
// delete dashboard - no messages expected
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
doDelete("/api/dashboard/" + savedDashboard.getUuidId()) |
||||
|
.andExpect(status().isOk()); |
||||
|
Assert.assertFalse(edgeImitator.waitForMessages(1)); |
||||
|
|
||||
|
// create dashboard #2 and assign to edge
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
dashboard = new Dashboard(); |
||||
|
dashboard.setTitle("Edge Test Dashboard #2"); |
||||
|
savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class); |
||||
|
doPost("/api/edge/" + edge.getUuidId() |
||||
|
+ "/dashboard/" + savedDashboard.getUuidId(), Dashboard.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof DashboardUpdateMsg); |
||||
|
dashboardUpdateMsg = (DashboardUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedDashboard.getUuidId().getMostSignificantBits(), dashboardUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(savedDashboard.getUuidId().getLeastSignificantBits(), dashboardUpdateMsg.getIdLSB()); |
||||
|
Assert.assertEquals(savedDashboard.getTitle(), dashboardUpdateMsg.getTitle()); |
||||
|
|
||||
|
// assign dashboard #2 to customer
|
||||
|
Customer customer = new Customer(); |
||||
|
customer.setTitle("Edge Customer"); |
||||
|
Customer savedCustomer = doPost("/api/customer", customer, Customer.class); |
||||
|
edgeImitator.expectMessageAmount(2); |
||||
|
doPost("/api/customer/" + savedCustomer.getUuidId() |
||||
|
+ "/edge/" + edge.getUuidId(), Edge.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
doPost("/api/customer/" + savedCustomer.getUuidId() |
||||
|
+ "/dashboard/" + savedDashboard.getUuidId(), Dashboard.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof DashboardUpdateMsg); |
||||
|
dashboardUpdateMsg = (DashboardUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); |
||||
|
Set<ShortCustomerInfo> assignedCustomers = |
||||
|
JacksonUtil.fromString(dashboardUpdateMsg.getAssignedCustomers(), new TypeReference<>() {}); |
||||
|
Assert.assertNotNull(assignedCustomers); |
||||
|
Assert.assertFalse(assignedCustomers.isEmpty()); |
||||
|
Assert.assertTrue(assignedCustomers.contains(new ShortCustomerInfo(savedCustomer.getId(), customer.getTitle(), customer.isPublic()))); |
||||
|
|
||||
|
// unassign dashboard #2 from customer
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
doDelete("/api/customer/" + savedCustomer.getUuidId() |
||||
|
+ "/dashboard/" + savedDashboard.getUuidId(), Dashboard.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof DashboardUpdateMsg); |
||||
|
dashboardUpdateMsg = (DashboardUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); |
||||
|
assignedCustomers = |
||||
|
JacksonUtil.fromString(dashboardUpdateMsg.getAssignedCustomers(), new TypeReference<>() {}); |
||||
|
Assert.assertNotNull(assignedCustomers); |
||||
|
Assert.assertTrue(assignedCustomers.isEmpty()); |
||||
|
|
||||
|
// delete dashboard #2 - messages expected
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
doDelete("/api/dashboard/" + savedDashboard.getUuidId()) |
||||
|
.andExpect(status().isOk()); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof DashboardUpdateMsg); |
||||
|
dashboardUpdateMsg = (DashboardUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedDashboard.getUuidId().getMostSignificantBits(), dashboardUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(savedDashboard.getUuidId().getLeastSignificantBits(), dashboardUpdateMsg.getIdLSB()); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,627 @@ |
|||||
|
/** |
||||
|
* 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.edge; |
||||
|
|
||||
|
import com.datastax.oss.driver.api.core.uuid.Uuids; |
||||
|
import com.fasterxml.jackson.core.type.TypeReference; |
||||
|
import com.fasterxml.jackson.databind.JsonNode; |
||||
|
import com.fasterxml.jackson.databind.node.ObjectNode; |
||||
|
import com.google.gson.JsonObject; |
||||
|
import com.google.protobuf.AbstractMessage; |
||||
|
import io.netty.handler.codec.mqtt.MqttQoS; |
||||
|
import org.awaitility.Awaitility; |
||||
|
import org.junit.Assert; |
||||
|
import org.junit.Test; |
||||
|
import org.springframework.test.context.TestPropertySource; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.common.data.Customer; |
||||
|
import org.thingsboard.server.common.data.DataConstants; |
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
import org.thingsboard.server.common.data.EntityType; |
||||
|
import org.thingsboard.server.common.data.StringUtils; |
||||
|
import org.thingsboard.server.common.data.TenantProfile; |
||||
|
import org.thingsboard.server.common.data.edge.Edge; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEvent; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventActionType; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventType; |
||||
|
import org.thingsboard.server.common.data.id.CustomerId; |
||||
|
import org.thingsboard.server.common.data.id.EntityId; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.common.data.security.DeviceCredentials; |
||||
|
import org.thingsboard.server.common.data.security.DeviceCredentialsType; |
||||
|
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; |
||||
|
import org.thingsboard.server.common.transport.adaptor.JsonConverter; |
||||
|
import org.thingsboard.server.gen.edge.v1.AttributesRequestMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.DeviceCredentialsRequestMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.EntityDataProto; |
||||
|
import org.thingsboard.server.gen.edge.v1.RpcResponseMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; |
||||
|
import org.thingsboard.server.gen.edge.v1.UplinkMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.UplinkResponseMsg; |
||||
|
import org.thingsboard.server.gen.transport.TransportProtos; |
||||
|
import org.thingsboard.server.transport.mqtt.MqttTestCallback; |
||||
|
import org.thingsboard.server.transport.mqtt.MqttTestClient; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
import java.util.Optional; |
||||
|
import java.util.Random; |
||||
|
import java.util.UUID; |
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
|
||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
|
||||
|
@TestPropertySource(properties = { |
||||
|
"transport.mqtt.enabled=true" |
||||
|
}) |
||||
|
abstract public class BaseDeviceEdgeTest extends AbstractEdgeTest { |
||||
|
|
||||
|
@Test |
||||
|
public void testDevices() throws Exception { |
||||
|
// create device and assign to edge; update device
|
||||
|
Device savedDevice = saveDeviceOnCloudAndVerifyDeliveryToEdge(); |
||||
|
|
||||
|
// unassign device from edge
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
doDelete("/api/edge/" + edge.getUuidId() |
||||
|
+ "/device/" + savedDevice.getUuidId(), Device.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
AbstractMessage latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof DeviceUpdateMsg); |
||||
|
DeviceUpdateMsg deviceUpdateMsg = (DeviceUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, deviceUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedDevice.getUuidId().getMostSignificantBits(), deviceUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(savedDevice.getUuidId().getLeastSignificantBits(), deviceUpdateMsg.getIdLSB()); |
||||
|
|
||||
|
// delete device - no messages expected
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
doDelete("/api/device/" + savedDevice.getUuidId()) |
||||
|
.andExpect(status().isOk()); |
||||
|
Assert.assertFalse(edgeImitator.waitForMessages(1)); |
||||
|
|
||||
|
// create device #2 and assign to edge
|
||||
|
edgeImitator.expectMessageAmount(2); |
||||
|
savedDevice = saveDevice("Edge Device 3", "Default"); |
||||
|
doPost("/api/edge/" + edge.getUuidId() |
||||
|
+ "/device/" + savedDevice.getUuidId(), Device.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
Optional<DeviceUpdateMsg> deviceUpdateMsgOpt = edgeImitator.findMessageByType(DeviceUpdateMsg.class); |
||||
|
Assert.assertTrue(deviceUpdateMsgOpt.isPresent()); |
||||
|
deviceUpdateMsg = deviceUpdateMsgOpt.get(); |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedDevice.getUuidId().getMostSignificantBits(), deviceUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(savedDevice.getUuidId().getLeastSignificantBits(), deviceUpdateMsg.getIdLSB()); |
||||
|
Assert.assertEquals(savedDevice.getName(), deviceUpdateMsg.getName()); |
||||
|
Assert.assertEquals(savedDevice.getType(), deviceUpdateMsg.getType()); |
||||
|
|
||||
|
Optional<DeviceProfileUpdateMsg> deviceProfileUpdateMsgOpt = edgeImitator.findMessageByType(DeviceProfileUpdateMsg.class); |
||||
|
Assert.assertTrue(deviceProfileUpdateMsgOpt.isPresent()); |
||||
|
DeviceProfileUpdateMsg deviceProfileUpdateMsg = deviceProfileUpdateMsgOpt.get(); |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedDevice.getDeviceProfileId().getId().getMostSignificantBits(), deviceProfileUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(savedDevice.getDeviceProfileId().getId().getLeastSignificantBits(), deviceProfileUpdateMsg.getIdLSB()); |
||||
|
|
||||
|
// assign device #2 to customer
|
||||
|
Customer customer = new Customer(); |
||||
|
customer.setTitle("Edge Customer"); |
||||
|
Customer savedCustomer = doPost("/api/customer", customer, Customer.class); |
||||
|
edgeImitator.expectMessageAmount(2); |
||||
|
doPost("/api/customer/" + savedCustomer.getUuidId() |
||||
|
+ "/edge/" + edge.getUuidId(), Edge.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
doPost("/api/customer/" + savedCustomer.getUuidId() |
||||
|
+ "/device/" + savedDevice.getUuidId(), Device.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof DeviceUpdateMsg); |
||||
|
deviceUpdateMsg = (DeviceUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, deviceUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedCustomer.getUuidId().getMostSignificantBits(), deviceUpdateMsg.getCustomerIdMSB()); |
||||
|
Assert.assertEquals(savedCustomer.getUuidId().getLeastSignificantBits(), deviceUpdateMsg.getCustomerIdLSB()); |
||||
|
|
||||
|
// unassign device #2 from customer
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
doDelete("/api/customer/device/" + savedDevice.getUuidId(), Device.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof DeviceUpdateMsg); |
||||
|
deviceUpdateMsg = (DeviceUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, deviceUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals( |
||||
|
new CustomerId(EntityId.NULL_UUID), |
||||
|
new CustomerId(new UUID(deviceUpdateMsg.getCustomerIdMSB(), deviceUpdateMsg.getCustomerIdLSB()))); |
||||
|
|
||||
|
// delete device #2 - messages expected
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
doDelete("/api/device/" + savedDevice.getUuidId()) |
||||
|
.andExpect(status().isOk()); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof DeviceUpdateMsg); |
||||
|
deviceUpdateMsg = (DeviceUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, deviceUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(savedDevice.getUuidId().getMostSignificantBits(), deviceUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(savedDevice.getUuidId().getLeastSignificantBits(), deviceUpdateMsg.getIdLSB()); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testUpdateDeviceCredentials() throws Exception { |
||||
|
// create device and assign to edge; update device
|
||||
|
Device savedDevice = saveDeviceOnCloudAndVerifyDeliveryToEdge(); |
||||
|
|
||||
|
// update device credentials - ACCESS_TOKEN
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
DeviceCredentials deviceCredentials = |
||||
|
doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); |
||||
|
Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId()); |
||||
|
deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN); |
||||
|
deviceCredentials.setCredentialsId("access_token"); |
||||
|
doPost("/api/device/credentials", deviceCredentials) |
||||
|
.andExpect(status().isOk()); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
AbstractMessage latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof DeviceCredentialsUpdateMsg); |
||||
|
DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg = (DeviceCredentialsUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), deviceCredentialsUpdateMsg.getCredentialsType()); |
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsId(), deviceCredentialsUpdateMsg.getCredentialsId()); |
||||
|
Assert.assertFalse(deviceCredentialsUpdateMsg.hasCredentialsValue()); |
||||
|
|
||||
|
// update device credentials - X509_CERTIFICATE
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
deviceCredentials.setCredentialsType(DeviceCredentialsType.X509_CERTIFICATE); |
||||
|
deviceCredentials.setCredentialsId(null); |
||||
|
deviceCredentials.setCredentialsValue("-----BEGIN RSA PRIVATE KEY-----"); |
||||
|
doPost("/api/device/credentials", deviceCredentials) |
||||
|
.andExpect(status().isOk()); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof DeviceCredentialsUpdateMsg); |
||||
|
deviceCredentialsUpdateMsg = (DeviceCredentialsUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), deviceCredentialsUpdateMsg.getCredentialsType()); |
||||
|
Assert.assertFalse(deviceCredentialsUpdateMsg.getCredentialsId().isEmpty()); |
||||
|
Assert.assertTrue(deviceCredentialsUpdateMsg.hasCredentialsValue()); |
||||
|
Assert.assertEquals(deviceCredentials.getCredentialsValue(), deviceCredentialsUpdateMsg.getCredentialsValue()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testDeviceReachedMaximumAllowedOnCloud() throws Exception { |
||||
|
// update tenant profile configuration
|
||||
|
loginSysAdmin(); |
||||
|
TenantProfile tenantProfile = doGet("/api/tenantProfile/" + savedTenant.getTenantProfileId().getId(), TenantProfile.class); |
||||
|
DefaultTenantProfileConfiguration profileConfiguration = |
||||
|
(DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration(); |
||||
|
profileConfiguration.setMaxDevices(1); |
||||
|
tenantProfile.getProfileData().setConfiguration(profileConfiguration); |
||||
|
doPost("/api/tenantProfile/", tenantProfile, TenantProfile.class); |
||||
|
|
||||
|
loginTenantAdmin(); |
||||
|
|
||||
|
UUID uuid = Uuids.timeBased(); |
||||
|
|
||||
|
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); |
||||
|
DeviceUpdateMsg.Builder deviceUpdateMsgBuilder = DeviceUpdateMsg.newBuilder(); |
||||
|
deviceUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); |
||||
|
deviceUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); |
||||
|
deviceUpdateMsgBuilder.setName("Edge Device"); |
||||
|
deviceUpdateMsgBuilder.setType("default"); |
||||
|
deviceUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); |
||||
|
uplinkMsgBuilder.addDeviceUpdateMsg(deviceUpdateMsgBuilder.build()); |
||||
|
|
||||
|
edgeImitator.expectResponsesAmount(1); |
||||
|
|
||||
|
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); |
||||
|
|
||||
|
Assert.assertTrue(edgeImitator.waitForResponses()); |
||||
|
|
||||
|
UplinkResponseMsg latestResponseMsg = edgeImitator.getLatestResponseMsg(); |
||||
|
Assert.assertTrue(latestResponseMsg.getSuccess()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSendDeviceRpcResponseToCloud() throws Exception { |
||||
|
Device device = findDeviceByName("Edge Device 1"); |
||||
|
|
||||
|
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); |
||||
|
DeviceRpcCallMsg.Builder deviceRpcCallResponseBuilder = DeviceRpcCallMsg.newBuilder(); |
||||
|
deviceRpcCallResponseBuilder.setDeviceIdMSB(device.getUuidId().getMostSignificantBits()); |
||||
|
deviceRpcCallResponseBuilder.setDeviceIdLSB(device.getUuidId().getLeastSignificantBits()); |
||||
|
deviceRpcCallResponseBuilder.setOneway(true); |
||||
|
deviceRpcCallResponseBuilder.setRequestId(0); |
||||
|
deviceRpcCallResponseBuilder.setExpirationTime(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(10)); |
||||
|
RpcResponseMsg.Builder responseBuilder = |
||||
|
RpcResponseMsg.newBuilder().setResponse("{}"); |
||||
|
testAutoGeneratedCodeByProtobuf(responseBuilder); |
||||
|
|
||||
|
deviceRpcCallResponseBuilder.setResponseMsg(responseBuilder.build()); |
||||
|
testAutoGeneratedCodeByProtobuf(deviceRpcCallResponseBuilder); |
||||
|
|
||||
|
uplinkMsgBuilder.addDeviceRpcCallMsg(deviceRpcCallResponseBuilder.build()); |
||||
|
testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder); |
||||
|
|
||||
|
edgeImitator.expectResponsesAmount(1); |
||||
|
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); |
||||
|
Assert.assertTrue(edgeImitator.waitForResponses()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSendDeviceCredentialsUpdateToCloud() throws Exception { |
||||
|
Device device = findDeviceByName("Edge Device 1"); |
||||
|
|
||||
|
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); |
||||
|
DeviceCredentialsUpdateMsg.Builder deviceCredentialsUpdateMsgBuilder = DeviceCredentialsUpdateMsg.newBuilder(); |
||||
|
deviceCredentialsUpdateMsgBuilder.setDeviceIdMSB(device.getUuidId().getMostSignificantBits()); |
||||
|
deviceCredentialsUpdateMsgBuilder.setDeviceIdLSB(device.getUuidId().getLeastSignificantBits()); |
||||
|
deviceCredentialsUpdateMsgBuilder.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN.name()); |
||||
|
deviceCredentialsUpdateMsgBuilder.setCredentialsId("NEW_TOKEN"); |
||||
|
testAutoGeneratedCodeByProtobuf(deviceCredentialsUpdateMsgBuilder); |
||||
|
uplinkMsgBuilder.addDeviceCredentialsUpdateMsg(deviceCredentialsUpdateMsgBuilder.build()); |
||||
|
|
||||
|
testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder); |
||||
|
|
||||
|
edgeImitator.expectResponsesAmount(1); |
||||
|
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); |
||||
|
Assert.assertTrue(edgeImitator.waitForResponses()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSendDeviceCredentialsRequestToCloud() throws Exception { |
||||
|
Device device = findDeviceByName("Edge Device 1"); |
||||
|
|
||||
|
DeviceCredentials deviceCredentials = doGet("/api/device/" + device.getUuidId() + "/credentials", DeviceCredentials.class); |
||||
|
|
||||
|
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); |
||||
|
DeviceCredentialsRequestMsg.Builder deviceCredentialsRequestMsgBuilder = DeviceCredentialsRequestMsg.newBuilder(); |
||||
|
deviceCredentialsRequestMsgBuilder.setDeviceIdMSB(device.getUuidId().getMostSignificantBits()); |
||||
|
deviceCredentialsRequestMsgBuilder.setDeviceIdLSB(device.getUuidId().getLeastSignificantBits()); |
||||
|
testAutoGeneratedCodeByProtobuf(deviceCredentialsRequestMsgBuilder); |
||||
|
uplinkMsgBuilder.addDeviceCredentialsRequestMsg(deviceCredentialsRequestMsgBuilder.build()); |
||||
|
|
||||
|
testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder); |
||||
|
|
||||
|
edgeImitator.expectResponsesAmount(1); |
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); |
||||
|
Assert.assertTrue(edgeImitator.waitForResponses()); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
|
||||
|
AbstractMessage latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof DeviceCredentialsUpdateMsg); |
||||
|
DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg = (DeviceCredentialsUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(deviceCredentialsUpdateMsg.getDeviceIdMSB(), device.getUuidId().getMostSignificantBits()); |
||||
|
Assert.assertEquals(deviceCredentialsUpdateMsg.getDeviceIdLSB(), device.getUuidId().getLeastSignificantBits()); |
||||
|
Assert.assertEquals(deviceCredentialsUpdateMsg.getCredentialsType(), deviceCredentials.getCredentialsType().name()); |
||||
|
Assert.assertEquals(deviceCredentialsUpdateMsg.getCredentialsId(), deviceCredentials.getCredentialsId()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSendAttributesRequestToCloud() throws Exception { |
||||
|
Device device = findDeviceByName("Edge Device 1"); |
||||
|
sendAttributesRequestAndVerify(device, DataConstants.SERVER_SCOPE, "{\"key1\":\"value1\"}", |
||||
|
"key1", "value1"); |
||||
|
sendAttributesRequestAndVerify(device, DataConstants.SHARED_SCOPE, "{\"key2\":\"value2\"}", |
||||
|
"key2", "value2"); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSendDeleteDeviceOnEdgeToCloud() throws Exception { |
||||
|
Device device = saveDeviceOnCloudAndVerifyDeliveryToEdge(); |
||||
|
UplinkMsg.Builder upLinkMsgBuilder = UplinkMsg.newBuilder(); |
||||
|
DeviceUpdateMsg.Builder deviceDeleteMsgBuilder = DeviceUpdateMsg.newBuilder(); |
||||
|
deviceDeleteMsgBuilder.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE); |
||||
|
deviceDeleteMsgBuilder.setIdMSB(device.getId().getId().getMostSignificantBits()); |
||||
|
deviceDeleteMsgBuilder.setIdLSB(device.getId().getId().getLeastSignificantBits()); |
||||
|
testAutoGeneratedCodeByProtobuf(deviceDeleteMsgBuilder); |
||||
|
|
||||
|
upLinkMsgBuilder.addDeviceUpdateMsg(deviceDeleteMsgBuilder.build()); |
||||
|
testAutoGeneratedCodeByProtobuf(upLinkMsgBuilder); |
||||
|
|
||||
|
edgeImitator.expectResponsesAmount(1); |
||||
|
edgeImitator.sendUplinkMsg(upLinkMsgBuilder.build()); |
||||
|
Assert.assertTrue(edgeImitator.waitForResponses()); |
||||
|
device = doGet("/api/device/" + device.getUuidId(), Device.class); |
||||
|
Assert.assertNotNull(device); |
||||
|
List<Device> edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getUuidId() + "/devices?", |
||||
|
new TypeReference<PageData<Device>>() { |
||||
|
}, new PageLink(100)).getData(); |
||||
|
Assert.assertFalse(edgeDevices.contains(device)); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSendTelemetryToCloud() throws Exception { |
||||
|
Device device = saveDeviceOnCloudAndVerifyDeliveryToEdge(); |
||||
|
|
||||
|
edgeImitator.expectResponsesAmount(2); |
||||
|
|
||||
|
JsonObject data = new JsonObject(); |
||||
|
String timeseriesKey = "key"; |
||||
|
String timeseriesValue = "25"; |
||||
|
data.addProperty(timeseriesKey, timeseriesValue); |
||||
|
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); |
||||
|
EntityDataProto.Builder entityDataBuilder = EntityDataProto.newBuilder(); |
||||
|
entityDataBuilder.setPostTelemetryMsg(JsonConverter.convertToTelemetryProto(data, System.currentTimeMillis())); |
||||
|
entityDataBuilder.setEntityType(device.getId().getEntityType().name()); |
||||
|
entityDataBuilder.setEntityIdMSB(device.getUuidId().getMostSignificantBits()); |
||||
|
entityDataBuilder.setEntityIdLSB(device.getUuidId().getLeastSignificantBits()); |
||||
|
testAutoGeneratedCodeByProtobuf(entityDataBuilder); |
||||
|
uplinkMsgBuilder.addEntityData(entityDataBuilder.build()); |
||||
|
|
||||
|
testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder); |
||||
|
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); |
||||
|
|
||||
|
JsonObject attributesData = new JsonObject(); |
||||
|
String attributesKey = "test_attr"; |
||||
|
String attributesValue = "test_value"; |
||||
|
attributesData.addProperty(attributesKey, attributesValue); |
||||
|
UplinkMsg.Builder uplinkMsgBuilder2 = UplinkMsg.newBuilder(); |
||||
|
EntityDataProto.Builder entityDataBuilder2 = EntityDataProto.newBuilder(); |
||||
|
entityDataBuilder2.setEntityType(device.getId().getEntityType().name()); |
||||
|
entityDataBuilder2.setEntityIdMSB(device.getId().getId().getMostSignificantBits()); |
||||
|
entityDataBuilder2.setEntityIdLSB(device.getId().getId().getLeastSignificantBits()); |
||||
|
entityDataBuilder2.setAttributesUpdatedMsg(JsonConverter.convertToAttributesProto(attributesData)); |
||||
|
entityDataBuilder2.setPostAttributeScope(DataConstants.SERVER_SCOPE); |
||||
|
|
||||
|
uplinkMsgBuilder2.addEntityData(entityDataBuilder2.build()); |
||||
|
|
||||
|
edgeImitator.sendUplinkMsg(uplinkMsgBuilder2.build()); |
||||
|
Assert.assertTrue(edgeImitator.waitForResponses()); |
||||
|
|
||||
|
Awaitility.await() |
||||
|
.atMost(2, TimeUnit.SECONDS) |
||||
|
.until(() -> loadDeviceTimeseries(device, timeseriesKey).containsKey(timeseriesKey)); |
||||
|
|
||||
|
Map<String, List<Map<String, String>>> timeseries = loadDeviceTimeseries(device, timeseriesKey); |
||||
|
Assert.assertTrue(timeseries.containsKey(timeseriesKey)); |
||||
|
Assert.assertEquals(1, timeseries.get(timeseriesKey).size()); |
||||
|
Assert.assertEquals(timeseriesValue, timeseries.get(timeseriesKey).get(0).get("value")); |
||||
|
|
||||
|
String attributeValuesUrl = "/api/plugins/telemetry/DEVICE/" + device.getId() + "/values/attributes/" + DataConstants.SERVER_SCOPE; |
||||
|
List<Map<String, String>> attributes = doGetAsyncTyped(attributeValuesUrl, new TypeReference<>() {}); |
||||
|
|
||||
|
Assert.assertEquals(3, attributes.size()); |
||||
|
|
||||
|
Optional<Map<String, String>> activeAttributeOpt = getAttributeByKey("active", attributes); |
||||
|
Assert.assertTrue(activeAttributeOpt.isPresent()); |
||||
|
Map<String, String> activeAttribute = activeAttributeOpt.get(); |
||||
|
Assert.assertEquals("true", activeAttribute.get("value")); |
||||
|
|
||||
|
Optional<Map<String, String>> customAttributeOpt = getAttributeByKey(attributesKey, attributes); |
||||
|
Assert.assertTrue(customAttributeOpt.isPresent()); |
||||
|
Map<String, String> customAttribute = customAttributeOpt.get(); |
||||
|
Assert.assertEquals(attributesValue, customAttribute.get("value")); |
||||
|
|
||||
|
doDelete("/api/plugins/telemetry/DEVICE/" + device.getId().getId() + "/SERVER_SCOPE?keys=" + attributesKey, String.class); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSendDeviceToCloudWithNameThatAlreadyExistsOnCloud() throws Exception { |
||||
|
String deviceOnCloudName = StringUtils.randomAlphanumeric(15); |
||||
|
Device deviceOnCloud = saveDevice(deviceOnCloudName, "Default"); |
||||
|
|
||||
|
UUID uuid = Uuids.timeBased(); |
||||
|
|
||||
|
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); |
||||
|
DeviceUpdateMsg.Builder deviceUpdateMsgBuilder = DeviceUpdateMsg.newBuilder(); |
||||
|
deviceUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); |
||||
|
deviceUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); |
||||
|
deviceUpdateMsgBuilder.setName(deviceOnCloudName); |
||||
|
deviceUpdateMsgBuilder.setType("test"); |
||||
|
deviceUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); |
||||
|
testAutoGeneratedCodeByProtobuf(deviceUpdateMsgBuilder); |
||||
|
uplinkMsgBuilder.addDeviceUpdateMsg(deviceUpdateMsgBuilder.build()); |
||||
|
|
||||
|
edgeImitator.expectResponsesAmount(1); |
||||
|
edgeImitator.expectMessageAmount(2); |
||||
|
testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder); |
||||
|
|
||||
|
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); |
||||
|
|
||||
|
Assert.assertTrue(edgeImitator.waitForResponses()); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
|
||||
|
AbstractMessage latestMessage = edgeImitator.getMessageFromTail(2); |
||||
|
Assert.assertTrue(latestMessage instanceof DeviceUpdateMsg); |
||||
|
DeviceUpdateMsg latestDeviceUpdateMsg = (DeviceUpdateMsg) latestMessage; |
||||
|
Assert.assertNotEquals(deviceOnCloudName, latestDeviceUpdateMsg.getName()); |
||||
|
Assert.assertEquals(deviceOnCloudName, latestDeviceUpdateMsg.getConflictName()); |
||||
|
|
||||
|
UUID newDeviceId = new UUID(latestDeviceUpdateMsg.getIdMSB(), latestDeviceUpdateMsg.getIdLSB()); |
||||
|
|
||||
|
Assert.assertNotEquals(deviceOnCloud.getId().getId(), newDeviceId); |
||||
|
|
||||
|
Device device = doGet("/api/device/" + newDeviceId, Device.class); |
||||
|
Assert.assertNotNull(device); |
||||
|
Assert.assertNotEquals(deviceOnCloudName, device.getName()); |
||||
|
|
||||
|
latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof DeviceCredentialsRequestMsg); |
||||
|
DeviceCredentialsRequestMsg latestDeviceCredentialsRequestMsg = (DeviceCredentialsRequestMsg) latestMessage; |
||||
|
Assert.assertEquals(uuid.getMostSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdMSB()); |
||||
|
Assert.assertEquals(uuid.getLeastSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdLSB()); |
||||
|
|
||||
|
newDeviceId = new UUID(latestDeviceCredentialsRequestMsg.getDeviceIdMSB(), latestDeviceCredentialsRequestMsg.getDeviceIdLSB()); |
||||
|
|
||||
|
device = doGet("/api/device/" + newDeviceId, Device.class); |
||||
|
Assert.assertNotNull(device); |
||||
|
Assert.assertNotEquals(deviceOnCloudName, device.getName()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSendDeviceToCloud() throws Exception { |
||||
|
UUID uuid = Uuids.timeBased(); |
||||
|
|
||||
|
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); |
||||
|
DeviceUpdateMsg.Builder deviceUpdateMsgBuilder = DeviceUpdateMsg.newBuilder(); |
||||
|
deviceUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); |
||||
|
deviceUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); |
||||
|
deviceUpdateMsgBuilder.setName("Edge Device 2"); |
||||
|
deviceUpdateMsgBuilder.setType("test"); |
||||
|
deviceUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); |
||||
|
uplinkMsgBuilder.addDeviceUpdateMsg(deviceUpdateMsgBuilder.build()); |
||||
|
|
||||
|
edgeImitator.expectResponsesAmount(1); |
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
|
||||
|
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); |
||||
|
|
||||
|
Assert.assertTrue(edgeImitator.waitForResponses()); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
|
||||
|
AbstractMessage latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof DeviceCredentialsRequestMsg); |
||||
|
DeviceCredentialsRequestMsg latestDeviceCredentialsRequestMsg = (DeviceCredentialsRequestMsg) latestMessage; |
||||
|
Assert.assertEquals(uuid.getMostSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdMSB()); |
||||
|
Assert.assertEquals(uuid.getLeastSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdLSB()); |
||||
|
|
||||
|
UUID newDeviceId = new UUID(latestDeviceCredentialsRequestMsg.getDeviceIdMSB(), latestDeviceCredentialsRequestMsg.getDeviceIdLSB()); |
||||
|
|
||||
|
Device device = doGet("/api/device/" + newDeviceId, Device.class); |
||||
|
Assert.assertNotNull(device); |
||||
|
Assert.assertEquals("Edge Device 2", device.getName()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testRpcCall() throws Exception { |
||||
|
Device device = findDeviceByName("Edge Device 1"); |
||||
|
|
||||
|
ObjectNode body = mapper.createObjectNode(); |
||||
|
body.put("requestId", new Random().nextInt()); |
||||
|
body.put("requestUUID", Uuids.timeBased().toString()); |
||||
|
body.put("oneway", false); |
||||
|
body.put("expirationTime", System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(10)); |
||||
|
body.put("method", "test_method"); |
||||
|
body.put("params", "{\"param1\":\"value1\"}"); |
||||
|
|
||||
|
EdgeEvent edgeEvent = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.RPC_CALL, device.getId().getId(), EdgeEventType.DEVICE, body); |
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
edgeEventService.saveAsync(edgeEvent).get(); |
||||
|
clusterService.onEdgeEventUpdate(tenantId, edge.getId()); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
|
||||
|
AbstractMessage latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof DeviceRpcCallMsg); |
||||
|
DeviceRpcCallMsg latestDeviceRpcCallMsg = (DeviceRpcCallMsg) latestMessage; |
||||
|
Assert.assertEquals("test_method", latestDeviceRpcCallMsg.getRequestMsg().getMethod()); |
||||
|
} |
||||
|
|
||||
|
private void sendAttributesRequestAndVerify(Device device, String scope, String attributesDataStr, String expectedKey, |
||||
|
String expectedValue) throws Exception { |
||||
|
JsonNode attributesData = mapper.readTree(attributesDataStr); |
||||
|
|
||||
|
doPost("/api/plugins/telemetry/DEVICE/" + device.getUuidId() + "/attributes/" + scope, |
||||
|
attributesData); |
||||
|
|
||||
|
// Wait before device attributes saved to database before requesting them from edge
|
||||
|
Awaitility.await() |
||||
|
.atMost(10, TimeUnit.SECONDS) |
||||
|
.until(() -> { |
||||
|
String urlTemplate = "/api/plugins/telemetry/DEVICE/" + device.getId() + "/keys/attributes/" + scope; |
||||
|
List<String> actualKeys = doGetAsyncTyped(urlTemplate, new TypeReference<>() {}); |
||||
|
return actualKeys != null && !actualKeys.isEmpty() && actualKeys.contains(expectedKey); |
||||
|
}); |
||||
|
|
||||
|
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); |
||||
|
AttributesRequestMsg.Builder attributesRequestMsgBuilder = AttributesRequestMsg.newBuilder(); |
||||
|
attributesRequestMsgBuilder.setEntityIdMSB(device.getUuidId().getMostSignificantBits()); |
||||
|
attributesRequestMsgBuilder.setEntityIdLSB(device.getUuidId().getLeastSignificantBits()); |
||||
|
attributesRequestMsgBuilder.setEntityType(EntityType.DEVICE.name()); |
||||
|
attributesRequestMsgBuilder.setScope(scope); |
||||
|
testAutoGeneratedCodeByProtobuf(attributesRequestMsgBuilder); |
||||
|
uplinkMsgBuilder.addAttributesRequestMsg(attributesRequestMsgBuilder.build()); |
||||
|
testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder); |
||||
|
|
||||
|
edgeImitator.expectResponsesAmount(1); |
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); |
||||
|
Assert.assertTrue(edgeImitator.waitForResponses()); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
|
||||
|
AbstractMessage latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof EntityDataProto); |
||||
|
EntityDataProto latestEntityDataMsg = (EntityDataProto) latestMessage; |
||||
|
Assert.assertEquals(device.getUuidId().getMostSignificantBits(), latestEntityDataMsg.getEntityIdMSB()); |
||||
|
Assert.assertEquals(device.getUuidId().getLeastSignificantBits(), latestEntityDataMsg.getEntityIdLSB()); |
||||
|
Assert.assertEquals(device.getId().getEntityType().name(), latestEntityDataMsg.getEntityType()); |
||||
|
Assert.assertEquals(scope, latestEntityDataMsg.getPostAttributeScope()); |
||||
|
Assert.assertTrue(latestEntityDataMsg.hasAttributesUpdatedMsg()); |
||||
|
|
||||
|
TransportProtos.PostAttributeMsg attributesUpdatedMsg = latestEntityDataMsg.getAttributesUpdatedMsg(); |
||||
|
|
||||
|
boolean found = false; |
||||
|
for (TransportProtos.KeyValueProto keyValueProto : attributesUpdatedMsg.getKvList()) { |
||||
|
if (keyValueProto.getKey().equals(expectedKey)) { |
||||
|
Assert.assertEquals(expectedKey, keyValueProto.getKey()); |
||||
|
Assert.assertEquals(expectedValue, keyValueProto.getStringV()); |
||||
|
found = true; |
||||
|
} |
||||
|
} |
||||
|
Assert.assertTrue("Expected key and value must be found", found); |
||||
|
} |
||||
|
|
||||
|
private Optional<Map<String, String>> getAttributeByKey(String key, List<Map<String, String>> attributes) { |
||||
|
return attributes.stream().filter(kv -> kv.get("key").equals(key)).findFirst(); |
||||
|
} |
||||
|
|
||||
|
private Map<String, List<Map<String, String>>> loadDeviceTimeseries(Device device, String timeseriesKey) throws Exception { |
||||
|
return doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + device.getUuidId() + "/values/timeseries?keys=" + timeseriesKey, |
||||
|
new TypeReference<>() {}); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void sendUpdateSharedAttributeToCloudAndValidateDeviceSubscription() throws Exception { |
||||
|
Device device = saveDeviceOnCloudAndVerifyDeliveryToEdge(); |
||||
|
|
||||
|
DeviceCredentials deviceCredentials = doGet("/api/device/" + device.getUuidId() + "/credentials", DeviceCredentials.class); |
||||
|
|
||||
|
MqttTestClient client = new MqttTestClient(); |
||||
|
client.connectAndWait(deviceCredentials.getCredentialsId()); |
||||
|
MqttTestCallback onUpdateCallback = new MqttTestCallback(); |
||||
|
client.setCallback(onUpdateCallback); |
||||
|
client.subscribeAndWait("v1/devices/me/attributes", MqttQoS.AT_MOST_ONCE); |
||||
|
|
||||
|
edgeImitator.expectResponsesAmount(1); |
||||
|
|
||||
|
JsonObject attributesData = new JsonObject(); |
||||
|
String attrKey = "sharedAttrName"; |
||||
|
String attrValue = "sharedAttrValue"; |
||||
|
attributesData.addProperty(attrKey, attrValue); |
||||
|
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); |
||||
|
EntityDataProto.Builder entityDataBuilder = EntityDataProto.newBuilder(); |
||||
|
entityDataBuilder.setEntityType(device.getId().getEntityType().name()); |
||||
|
entityDataBuilder.setEntityIdMSB(device.getId().getId().getMostSignificantBits()); |
||||
|
entityDataBuilder.setEntityIdLSB(device.getId().getId().getLeastSignificantBits()); |
||||
|
entityDataBuilder.setAttributesUpdatedMsg(JsonConverter.convertToAttributesProto(attributesData)); |
||||
|
entityDataBuilder.setPostAttributeScope(DataConstants.SHARED_SCOPE); |
||||
|
uplinkMsgBuilder.addEntityData(entityDataBuilder.build()); |
||||
|
|
||||
|
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); |
||||
|
Assert.assertTrue(edgeImitator.waitForResponses()); |
||||
|
|
||||
|
Assert.assertTrue(onUpdateCallback.getSubscribeLatch().await(5, TimeUnit.SECONDS)); |
||||
|
|
||||
|
Assert.assertEquals(JacksonUtil.OBJECT_MAPPER.createObjectNode().put(attrKey, attrValue), |
||||
|
JacksonUtil.fromBytes(onUpdateCallback.getPayloadBytes())); |
||||
|
|
||||
|
client.disconnect(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,319 @@ |
|||||
|
/** |
||||
|
* 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.edge; |
||||
|
|
||||
|
import com.google.protobuf.AbstractMessage; |
||||
|
import org.junit.Assert; |
||||
|
import org.junit.Test; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.common.data.DeviceProfile; |
||||
|
import org.thingsboard.server.common.data.DeviceTransportType; |
||||
|
import org.thingsboard.server.common.data.OtaPackageInfo; |
||||
|
import org.thingsboard.server.common.data.device.data.PowerMode; |
||||
|
import org.thingsboard.server.common.data.device.data.PowerSavingConfiguration; |
||||
|
import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration; |
||||
|
import org.thingsboard.server.common.data.device.profile.DefaultCoapDeviceTypeConfiguration; |
||||
|
import org.thingsboard.server.common.data.device.profile.DeviceProfileData; |
||||
|
import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; |
||||
|
import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; |
||||
|
import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; |
||||
|
import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration; |
||||
|
import org.thingsboard.server.common.data.device.profile.lwm2m.OtherConfiguration; |
||||
|
import org.thingsboard.server.common.data.device.profile.lwm2m.TelemetryMappingConfiguration; |
||||
|
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.AbstractLwM2MBootstrapServerCredential; |
||||
|
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBootstrapServerCredential; |
||||
|
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.NoSecLwM2MBootstrapServerCredential; |
||||
|
import org.thingsboard.server.common.data.kv.DataType; |
||||
|
import org.thingsboard.server.common.data.transport.snmp.SnmpMapping; |
||||
|
import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig; |
||||
|
import org.thingsboard.server.common.data.transport.snmp.config.impl.TelemetryQueryingSnmpCommunicationConfig; |
||||
|
import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; |
||||
|
import org.thingsboard.server.transport.AbstractTransportIntegrationTest; |
||||
|
import org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
import java.util.Optional; |
||||
|
|
||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
|
||||
|
abstract public class BaseDeviceProfileEdgeTest extends AbstractEdgeTest { |
||||
|
|
||||
|
@Test |
||||
|
public void testDeviceProfiles() throws Exception { |
||||
|
// create device profile
|
||||
|
DeviceProfile deviceProfile = this.createDeviceProfile("ONE_MORE_DEVICE_PROFILE", null); |
||||
|
extendDeviceProfileData(deviceProfile); |
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
deviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
AbstractMessage latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg); |
||||
|
DeviceProfileUpdateMsg deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(deviceProfile.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(deviceProfile.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getIdLSB()); |
||||
|
|
||||
|
// update device profile
|
||||
|
OtaPackageInfo firmwareOtaPackageInfo = saveOtaPackageInfo(deviceProfile.getId()); |
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
|
||||
|
deviceProfile.setFirmwareId(firmwareOtaPackageInfo.getId()); |
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
deviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg); |
||||
|
deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(firmwareOtaPackageInfo.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getFirmwareIdMSB()); |
||||
|
Assert.assertEquals(firmwareOtaPackageInfo.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getFirmwareIdLSB()); |
||||
|
|
||||
|
// delete profile
|
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
doDelete("/api/deviceProfile/" + deviceProfile.getUuidId()) |
||||
|
.andExpect(status().isOk()); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg); |
||||
|
deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(deviceProfile.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(deviceProfile.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getIdLSB()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testDeviceProfiles_snmp() throws Exception { |
||||
|
DeviceProfile deviceProfile = createDeviceProfileAndDoBasicAssert("SNMP", createSnmpDeviceProfileTransportConfiguration()); |
||||
|
|
||||
|
AbstractMessage latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg); |
||||
|
DeviceProfileUpdateMsg deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(deviceProfile.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(deviceProfile.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getIdLSB()); |
||||
|
Assert.assertEquals(DeviceTransportType.SNMP.name(), deviceProfileUpdateMsg.getTransportType()); |
||||
|
|
||||
|
Optional<DeviceProfileData> deviceProfileDataOpt = |
||||
|
dataDecodingEncodingService.decode(deviceProfileUpdateMsg.getProfileDataBytes().toByteArray()); |
||||
|
|
||||
|
Assert.assertTrue(deviceProfileDataOpt.isPresent()); |
||||
|
DeviceProfileData deviceProfileData = deviceProfileDataOpt.get(); |
||||
|
|
||||
|
Assert.assertTrue(deviceProfileData.getTransportConfiguration() instanceof SnmpDeviceProfileTransportConfiguration); |
||||
|
SnmpDeviceProfileTransportConfiguration transportConfiguration = |
||||
|
(SnmpDeviceProfileTransportConfiguration) deviceProfileData.getTransportConfiguration(); |
||||
|
Assert.assertEquals(Integer.valueOf(1000), transportConfiguration.getTimeoutMs()); |
||||
|
Assert.assertEquals(Integer.valueOf(3), transportConfiguration.getRetries()); |
||||
|
|
||||
|
Assert.assertFalse(transportConfiguration.getCommunicationConfigs().isEmpty()); |
||||
|
SnmpCommunicationConfig communicationConfig = transportConfiguration.getCommunicationConfigs().get(0); |
||||
|
Assert.assertTrue(communicationConfig instanceof TelemetryQueryingSnmpCommunicationConfig); |
||||
|
TelemetryQueryingSnmpCommunicationConfig snmpCommunicationConfig = |
||||
|
(TelemetryQueryingSnmpCommunicationConfig) communicationConfig; |
||||
|
|
||||
|
Assert.assertEquals(Long.valueOf(500L), snmpCommunicationConfig.getQueryingFrequencyMs()); |
||||
|
Assert.assertFalse(snmpCommunicationConfig.getMappings().isEmpty()); |
||||
|
|
||||
|
SnmpMapping snmpMapping = snmpCommunicationConfig.getMappings().get(0); |
||||
|
Assert.assertEquals("temperature", snmpMapping.getKey()); |
||||
|
Assert.assertEquals("1.3.3.5.6.7.8.9.1", snmpMapping.getOid()); |
||||
|
Assert.assertEquals(DataType.DOUBLE, snmpMapping.getDataType()); |
||||
|
|
||||
|
removeDeviceProfileAndDoBasicAssert(deviceProfile); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testDeviceProfiles_lwm2m() throws Exception { |
||||
|
DeviceProfile deviceProfile = createDeviceProfileAndDoBasicAssert("LWM2M", createLwm2mDeviceProfileTransportConfiguration()); |
||||
|
|
||||
|
AbstractMessage latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg); |
||||
|
DeviceProfileUpdateMsg deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(deviceProfile.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(deviceProfile.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getIdLSB()); |
||||
|
Assert.assertEquals(DeviceTransportType.LWM2M.name(), deviceProfileUpdateMsg.getTransportType()); |
||||
|
|
||||
|
Optional<DeviceProfileData> deviceProfileDataOpt = |
||||
|
dataDecodingEncodingService.decode(deviceProfileUpdateMsg.getProfileDataBytes().toByteArray()); |
||||
|
|
||||
|
Assert.assertTrue(deviceProfileDataOpt.isPresent()); |
||||
|
DeviceProfileData deviceProfileData = deviceProfileDataOpt.get(); |
||||
|
|
||||
|
Assert.assertTrue(deviceProfileData.getTransportConfiguration() instanceof Lwm2mDeviceProfileTransportConfiguration); |
||||
|
Lwm2mDeviceProfileTransportConfiguration transportConfiguration = |
||||
|
(Lwm2mDeviceProfileTransportConfiguration) deviceProfileData.getTransportConfiguration(); |
||||
|
|
||||
|
OtherConfiguration clientLwM2mSettings = transportConfiguration.getClientLwM2mSettings(); |
||||
|
Assert.assertEquals(PowerMode.DRX, clientLwM2mSettings.getPowerMode()); |
||||
|
Assert.assertEquals(Integer.valueOf(1), clientLwM2mSettings.getFwUpdateStrategy()); |
||||
|
Assert.assertEquals(Integer.valueOf(1), clientLwM2mSettings.getSwUpdateStrategy()); |
||||
|
Assert.assertEquals(Integer.valueOf(1), clientLwM2mSettings.getClientOnlyObserveAfterConnect()); |
||||
|
|
||||
|
Assert.assertTrue(transportConfiguration.isBootstrapServerUpdateEnable()); |
||||
|
|
||||
|
Assert.assertFalse(transportConfiguration.getBootstrap().isEmpty()); |
||||
|
LwM2MBootstrapServerCredential lwM2MBootstrapServerCredential = transportConfiguration.getBootstrap().get(0); |
||||
|
Assert.assertTrue(lwM2MBootstrapServerCredential instanceof NoSecLwM2MBootstrapServerCredential); |
||||
|
NoSecLwM2MBootstrapServerCredential noSecLwM2MBootstrapServerCredential = (NoSecLwM2MBootstrapServerCredential) lwM2MBootstrapServerCredential; |
||||
|
|
||||
|
Assert.assertEquals("PUBLIC_KEY", noSecLwM2MBootstrapServerCredential.getServerPublicKey()); |
||||
|
Assert.assertEquals(Integer.valueOf(123), noSecLwM2MBootstrapServerCredential.getShortServerId()); |
||||
|
Assert.assertTrue(noSecLwM2MBootstrapServerCredential.isBootstrapServerIs()); |
||||
|
Assert.assertEquals("localhost", noSecLwM2MBootstrapServerCredential.getHost()); |
||||
|
Assert.assertEquals(Integer.valueOf(5687), noSecLwM2MBootstrapServerCredential.getPort()); |
||||
|
|
||||
|
TelemetryMappingConfiguration observeAttr = transportConfiguration.getObserveAttr(); |
||||
|
Assert.assertEquals("batteryLevel", observeAttr.getKeyName().get("/3_1.0/0/9")); |
||||
|
Assert.assertTrue(observeAttr.getObserve().isEmpty()); |
||||
|
Assert.assertTrue(observeAttr.getAttribute().isEmpty()); |
||||
|
Assert.assertFalse(observeAttr.getTelemetry().isEmpty()); |
||||
|
Assert.assertTrue(observeAttr.getTelemetry().contains("/3_1.0/0/9")); |
||||
|
Assert.assertTrue(observeAttr.getAttributeLwm2m().isEmpty()); |
||||
|
|
||||
|
removeDeviceProfileAndDoBasicAssert(deviceProfile); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testDeviceProfiles_coap() throws Exception { |
||||
|
DeviceProfile deviceProfile = createDeviceProfileAndDoBasicAssert("COAP", createCoapDeviceProfileTransportConfiguration()); |
||||
|
|
||||
|
AbstractMessage latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg); |
||||
|
DeviceProfileUpdateMsg deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(deviceProfile.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(deviceProfile.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getIdLSB()); |
||||
|
Assert.assertEquals(DeviceTransportType.COAP.name(), deviceProfileUpdateMsg.getTransportType()); |
||||
|
|
||||
|
Optional<DeviceProfileData> deviceProfileDataOpt = |
||||
|
dataDecodingEncodingService.decode(deviceProfileUpdateMsg.getProfileDataBytes().toByteArray()); |
||||
|
|
||||
|
Assert.assertTrue(deviceProfileDataOpt.isPresent()); |
||||
|
DeviceProfileData deviceProfileData = deviceProfileDataOpt.get(); |
||||
|
|
||||
|
Assert.assertTrue(deviceProfileData.getTransportConfiguration() instanceof CoapDeviceProfileTransportConfiguration); |
||||
|
CoapDeviceProfileTransportConfiguration transportConfiguration = |
||||
|
(CoapDeviceProfileTransportConfiguration) deviceProfileData.getTransportConfiguration(); |
||||
|
|
||||
|
PowerSavingConfiguration clientSettings = transportConfiguration.getClientSettings(); |
||||
|
|
||||
|
Assert.assertEquals(PowerMode.DRX, clientSettings.getPowerMode()); |
||||
|
Assert.assertEquals(Long.valueOf(1L), clientSettings.getEdrxCycle()); |
||||
|
Assert.assertEquals(Long.valueOf(1L), clientSettings.getPsmActivityTimer()); |
||||
|
Assert.assertEquals(Long.valueOf(1L), clientSettings.getPagingTransmissionWindow()); |
||||
|
|
||||
|
Assert.assertTrue(transportConfiguration.getCoapDeviceTypeConfiguration() instanceof DefaultCoapDeviceTypeConfiguration); |
||||
|
DefaultCoapDeviceTypeConfiguration coapDeviceTypeConfiguration = |
||||
|
(DefaultCoapDeviceTypeConfiguration) transportConfiguration.getCoapDeviceTypeConfiguration(); |
||||
|
|
||||
|
Assert.assertTrue(coapDeviceTypeConfiguration.getTransportPayloadTypeConfiguration() instanceof ProtoTransportPayloadConfiguration); |
||||
|
|
||||
|
ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = |
||||
|
(ProtoTransportPayloadConfiguration) coapDeviceTypeConfiguration.getTransportPayloadTypeConfiguration(); |
||||
|
|
||||
|
Assert.assertEquals(AbstractTransportIntegrationTest.DEVICE_TELEMETRY_PROTO_SCHEMA, protoTransportPayloadConfiguration.getDeviceTelemetryProtoSchema()); |
||||
|
Assert.assertEquals(AbstractTransportIntegrationTest.DEVICE_ATTRIBUTES_PROTO_SCHEMA, protoTransportPayloadConfiguration.getDeviceAttributesProtoSchema()); |
||||
|
Assert.assertEquals(AbstractTransportIntegrationTest.DEVICE_RPC_RESPONSE_PROTO_SCHEMA, protoTransportPayloadConfiguration.getDeviceRpcResponseProtoSchema()); |
||||
|
Assert.assertEquals(AbstractTransportIntegrationTest.DEVICE_RPC_REQUEST_PROTO_SCHEMA, protoTransportPayloadConfiguration.getDeviceRpcRequestProtoSchema()); |
||||
|
|
||||
|
removeDeviceProfileAndDoBasicAssert(deviceProfile); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
private DeviceProfile createDeviceProfileAndDoBasicAssert(String deviceProfileName, DeviceProfileTransportConfiguration deviceProfileTransportConfiguration) throws Exception { |
||||
|
DeviceProfile deviceProfile = this.createDeviceProfile(deviceProfileName, deviceProfileTransportConfiguration); |
||||
|
extendDeviceProfileData(deviceProfile); |
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
deviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
return deviceProfile; |
||||
|
} |
||||
|
|
||||
|
private void removeDeviceProfileAndDoBasicAssert(DeviceProfile deviceProfile) throws Exception { |
||||
|
edgeImitator.expectMessageAmount(1); |
||||
|
doDelete("/api/deviceProfile/" + deviceProfile.getUuidId()) |
||||
|
.andExpect(status().isOk()); |
||||
|
Assert.assertTrue(edgeImitator.waitForMessages()); |
||||
|
AbstractMessage latestMessage = edgeImitator.getLatestMessage(); |
||||
|
Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg); |
||||
|
DeviceProfileUpdateMsg deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage; |
||||
|
Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType()); |
||||
|
Assert.assertEquals(deviceProfile.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getIdMSB()); |
||||
|
Assert.assertEquals(deviceProfile.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getIdLSB()); |
||||
|
} |
||||
|
|
||||
|
private SnmpDeviceProfileTransportConfiguration createSnmpDeviceProfileTransportConfiguration() { |
||||
|
SnmpDeviceProfileTransportConfiguration transportConfiguration = new SnmpDeviceProfileTransportConfiguration(); |
||||
|
List<SnmpCommunicationConfig> communicationConfigs = new ArrayList<>(); |
||||
|
TelemetryQueryingSnmpCommunicationConfig communicationConfig = new TelemetryQueryingSnmpCommunicationConfig(); |
||||
|
communicationConfig.setQueryingFrequencyMs(500L); |
||||
|
List<SnmpMapping> mappings = new ArrayList<>(); |
||||
|
mappings.add(new SnmpMapping("1.3.3.5.6.7.8.9.1", "temperature", DataType.DOUBLE)); |
||||
|
communicationConfig.setMappings(mappings); |
||||
|
communicationConfigs.add(communicationConfig); |
||||
|
transportConfiguration.setCommunicationConfigs(communicationConfigs); |
||||
|
transportConfiguration.setTimeoutMs(1000); |
||||
|
transportConfiguration.setRetries(3); |
||||
|
return transportConfiguration; |
||||
|
} |
||||
|
|
||||
|
private Lwm2mDeviceProfileTransportConfiguration createLwm2mDeviceProfileTransportConfiguration() { |
||||
|
Lwm2mDeviceProfileTransportConfiguration transportConfiguration = new Lwm2mDeviceProfileTransportConfiguration(); |
||||
|
|
||||
|
OtherConfiguration clientLwM2mSettings = JacksonUtil.fromString(AbstractLwM2MIntegrationTest.CLIENT_LWM2M_SETTINGS, OtherConfiguration.class); |
||||
|
transportConfiguration.setClientLwM2mSettings(clientLwM2mSettings); |
||||
|
|
||||
|
transportConfiguration.setBootstrapServerUpdateEnable(true); |
||||
|
|
||||
|
TelemetryMappingConfiguration observeAttrConfiguration = |
||||
|
JacksonUtil.fromString(AbstractLwM2MIntegrationTest.OBSERVE_ATTRIBUTES_WITH_PARAMS, TelemetryMappingConfiguration.class); |
||||
|
transportConfiguration.setObserveAttr(observeAttrConfiguration); |
||||
|
|
||||
|
List<LwM2MBootstrapServerCredential> bootstrap = new ArrayList<>(); |
||||
|
AbstractLwM2MBootstrapServerCredential bootstrapServerCredential = new NoSecLwM2MBootstrapServerCredential(); |
||||
|
bootstrapServerCredential.setServerPublicKey("PUBLIC_KEY"); |
||||
|
bootstrapServerCredential.setShortServerId(123); |
||||
|
bootstrapServerCredential.setBootstrapServerIs(true); |
||||
|
bootstrapServerCredential.setHost("localhost"); |
||||
|
bootstrapServerCredential.setPort(5687); |
||||
|
bootstrap.add(bootstrapServerCredential); |
||||
|
transportConfiguration.setBootstrap(bootstrap); |
||||
|
|
||||
|
return transportConfiguration; |
||||
|
} |
||||
|
|
||||
|
private CoapDeviceProfileTransportConfiguration createCoapDeviceProfileTransportConfiguration() { |
||||
|
CoapDeviceProfileTransportConfiguration transportConfiguration = new CoapDeviceProfileTransportConfiguration(); |
||||
|
PowerSavingConfiguration clientSettings = new PowerSavingConfiguration(); |
||||
|
clientSettings.setPowerMode(PowerMode.DRX); |
||||
|
clientSettings.setEdrxCycle(1L); |
||||
|
clientSettings.setPsmActivityTimer(1L); |
||||
|
clientSettings.setPagingTransmissionWindow(1L); |
||||
|
transportConfiguration.setClientSettings(clientSettings); |
||||
|
DefaultCoapDeviceTypeConfiguration coapDeviceTypeConfiguration = new DefaultCoapDeviceTypeConfiguration(); |
||||
|
ProtoTransportPayloadConfiguration transportPayloadTypeConfiguration = new ProtoTransportPayloadConfiguration(); |
||||
|
transportPayloadTypeConfiguration.setDeviceTelemetryProtoSchema(AbstractTransportIntegrationTest.DEVICE_TELEMETRY_PROTO_SCHEMA); |
||||
|
transportPayloadTypeConfiguration.setDeviceAttributesProtoSchema(AbstractTransportIntegrationTest.DEVICE_ATTRIBUTES_PROTO_SCHEMA); |
||||
|
transportPayloadTypeConfiguration.setDeviceRpcResponseProtoSchema(AbstractTransportIntegrationTest.DEVICE_RPC_RESPONSE_PROTO_SCHEMA); |
||||
|
transportPayloadTypeConfiguration.setDeviceRpcRequestProtoSchema(AbstractTransportIntegrationTest.DEVICE_RPC_REQUEST_PROTO_SCHEMA); |
||||
|
coapDeviceTypeConfiguration.setTransportPayloadTypeConfiguration(transportPayloadTypeConfiguration); |
||||
|
transportConfiguration.setCoapDeviceTypeConfiguration(coapDeviceTypeConfiguration); |
||||
|
return transportConfiguration; |
||||
|
} |
||||
|
} |
||||
File diff suppressed because it is too large
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue