Merge pull request #8000 from YuriyLytvynchuk/feature/node_deviceTypeSwitch
[3.4.4] Feature: new nodes 'asset type switch' & 'device type switch'
This commit is contained in:
		
						commit
						ef94482196
					
				@ -0,0 +1,45 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 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.rule.engine.filter;
 | 
			
		||||
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.thingsboard.rule.engine.api.EmptyNodeConfiguration;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbContext;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNode;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNodeException;
 | 
			
		||||
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
 | 
			
		||||
import org.thingsboard.server.common.data.id.EntityId;
 | 
			
		||||
import org.thingsboard.server.common.msg.TbMsg;
 | 
			
		||||
 | 
			
		||||
@Slf4j
 | 
			
		||||
public abstract class TbAbstractTypeSwitchNode implements TbNode {
 | 
			
		||||
 | 
			
		||||
    private EmptyNodeConfiguration config;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
 | 
			
		||||
        this.config = TbNodeUtils.convert(configuration, EmptyNodeConfiguration.class);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onMsg(TbContext ctx, TbMsg msg) throws TbNodeException {
 | 
			
		||||
        ctx.tellNext(msg, getRelationType(ctx, msg.getOriginator()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected abstract String getRelationType(TbContext ctx, EntityId originator) throws TbNodeException;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,54 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 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.rule.engine.filter;
 | 
			
		||||
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.thingsboard.rule.engine.api.EmptyNodeConfiguration;
 | 
			
		||||
import org.thingsboard.rule.engine.api.RuleNode;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbContext;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNodeException;
 | 
			
		||||
import org.thingsboard.server.common.data.EntityType;
 | 
			
		||||
import org.thingsboard.server.common.data.asset.AssetProfile;
 | 
			
		||||
import org.thingsboard.server.common.data.id.AssetId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.EntityId;
 | 
			
		||||
import org.thingsboard.server.common.data.plugin.ComponentType;
 | 
			
		||||
 | 
			
		||||
@Slf4j
 | 
			
		||||
@RuleNode(
 | 
			
		||||
        type = ComponentType.FILTER,
 | 
			
		||||
        name = "asset type switch",
 | 
			
		||||
        customRelations = true,
 | 
			
		||||
        relationTypes = {},
 | 
			
		||||
        configClazz = EmptyNodeConfiguration.class,
 | 
			
		||||
        nodeDescription = "Route incoming messages based on the name of the asset profile",
 | 
			
		||||
        nodeDetails = "Route incoming messages based on the name of the asset profile. The asset profile name is case-sensitive",
 | 
			
		||||
        uiResources = {"static/rulenode/rulenode-core-config.js"},
 | 
			
		||||
        configDirective = "tbNodeEmptyConfig")
 | 
			
		||||
public class TbAssetTypeSwitchNode extends TbAbstractTypeSwitchNode {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected String getRelationType(TbContext ctx, EntityId originator) throws TbNodeException {
 | 
			
		||||
        if (!EntityType.ASSET.equals(originator.getEntityType())) {
 | 
			
		||||
            throw new TbNodeException("Unsupported originator type: " + originator.getEntityType() + "! Only 'ASSET' type is allowed.");
 | 
			
		||||
        }
 | 
			
		||||
        AssetProfile assetProfile = ctx.getAssetProfileCache().get(ctx.getTenantId(), (AssetId) originator);
 | 
			
		||||
        if (assetProfile == null) {
 | 
			
		||||
            throw new TbNodeException("Asset profile for entity id: " + originator.getId() + " wasn't found!");
 | 
			
		||||
        }
 | 
			
		||||
        return assetProfile.getName();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,54 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 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.rule.engine.filter;
 | 
			
		||||
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.thingsboard.rule.engine.api.EmptyNodeConfiguration;
 | 
			
		||||
import org.thingsboard.rule.engine.api.RuleNode;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbContext;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNodeException;
 | 
			
		||||
import org.thingsboard.server.common.data.DeviceProfile;
 | 
			
		||||
import org.thingsboard.server.common.data.EntityType;
 | 
			
		||||
import org.thingsboard.server.common.data.id.DeviceId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.EntityId;
 | 
			
		||||
import org.thingsboard.server.common.data.plugin.ComponentType;
 | 
			
		||||
 | 
			
		||||
@Slf4j
 | 
			
		||||
@RuleNode(
 | 
			
		||||
        type = ComponentType.FILTER,
 | 
			
		||||
        name = "device type switch",
 | 
			
		||||
        customRelations = true,
 | 
			
		||||
        relationTypes = {"default"},
 | 
			
		||||
        configClazz = EmptyNodeConfiguration.class,
 | 
			
		||||
        nodeDescription = "Route incoming messages based on the name of the device profile",
 | 
			
		||||
        nodeDetails = "Route incoming messages based on the name of the device profile. The device profile name is case-sensitive",
 | 
			
		||||
        uiResources = {"static/rulenode/rulenode-core-config.js"},
 | 
			
		||||
        configDirective = "tbNodeEmptyConfig")
 | 
			
		||||
public class TbDeviceTypeSwitchNode extends TbAbstractTypeSwitchNode {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected String getRelationType(TbContext ctx, EntityId originator) throws TbNodeException {
 | 
			
		||||
        if (!EntityType.DEVICE.equals(originator.getEntityType())) {
 | 
			
		||||
            throw new TbNodeException("Unsupported originator type: " + originator.getEntityType() + "! Only 'DEVICE' type is allowed.");
 | 
			
		||||
        }
 | 
			
		||||
        DeviceProfile deviceProfile = ctx.getDeviceProfileCache().get(ctx.getTenantId(), (DeviceId) originator);
 | 
			
		||||
        if (deviceProfile == null) {
 | 
			
		||||
            throw new TbNodeException("Device profile for entity id: " + originator.getId() + " wasn't found!");
 | 
			
		||||
        }
 | 
			
		||||
        return deviceProfile.getName();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -29,11 +29,11 @@ import org.thingsboard.server.common.msg.TbMsg;
 | 
			
		||||
@Slf4j
 | 
			
		||||
@RuleNode(
 | 
			
		||||
        type = ComponentType.FILTER,
 | 
			
		||||
        name = "originator type",
 | 
			
		||||
        name = "entity type",
 | 
			
		||||
        configClazz = TbOriginatorTypeFilterNodeConfiguration.class,
 | 
			
		||||
        relationTypes = {"True", "False"},
 | 
			
		||||
        nodeDescription = "Filter incoming messages by message Originator Type",
 | 
			
		||||
        nodeDetails = "If Originator Type of incoming message is expected - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used.",
 | 
			
		||||
        nodeDetails = "If the entity type of the incoming message originator is expected - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used.",
 | 
			
		||||
        uiResources = {"static/rulenode/rulenode-core-config.js"},
 | 
			
		||||
        configDirective = "tbFilterNodeOriginatorTypeConfig")
 | 
			
		||||
public class TbOriginatorTypeFilterNode implements TbNode {
 | 
			
		||||
 | 
			
		||||
@ -19,37 +19,27 @@ import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.thingsboard.rule.engine.api.EmptyNodeConfiguration;
 | 
			
		||||
import org.thingsboard.rule.engine.api.RuleNode;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbContext;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNode;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNodeException;
 | 
			
		||||
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
 | 
			
		||||
import org.thingsboard.server.common.data.EntityType;
 | 
			
		||||
import org.thingsboard.server.common.data.id.EntityId;
 | 
			
		||||
import org.thingsboard.server.common.data.plugin.ComponentType;
 | 
			
		||||
import org.thingsboard.server.common.msg.TbMsg;
 | 
			
		||||
 | 
			
		||||
@Slf4j
 | 
			
		||||
@RuleNode(
 | 
			
		||||
        type = ComponentType.FILTER,
 | 
			
		||||
        name = "originator type switch",
 | 
			
		||||
        name = "entity type switch",
 | 
			
		||||
        configClazz = EmptyNodeConfiguration.class,
 | 
			
		||||
        relationTypes = {"Device", "Asset", "Alarm", "Entity View", "Tenant", "Customer", "User", "Dashboard", "Rule chain", "Rule node"},
 | 
			
		||||
        nodeDescription = "Route incoming messages by Message Originator Type",
 | 
			
		||||
        nodeDetails = "Routes messages to chain according to the originator type ('Device', 'Asset', etc.).",
 | 
			
		||||
        nodeDetails = "Routes messages to chain according to the entity type ('Device', 'Asset', etc.).",
 | 
			
		||||
        uiResources = {"static/rulenode/rulenode-core-config.js"},
 | 
			
		||||
        configDirective = "tbNodeEmptyConfig")
 | 
			
		||||
public class TbOriginatorTypeSwitchNode implements TbNode {
 | 
			
		||||
 | 
			
		||||
    EmptyNodeConfiguration config;
 | 
			
		||||
public class TbOriginatorTypeSwitchNode extends TbAbstractTypeSwitchNode {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
 | 
			
		||||
        this.config = TbNodeUtils.convert(configuration, EmptyNodeConfiguration.class);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onMsg(TbContext ctx, TbMsg msg) throws TbNodeException {
 | 
			
		||||
    protected String getRelationType(TbContext ctx, EntityId originator) throws TbNodeException {
 | 
			
		||||
        String relationType;
 | 
			
		||||
        EntityType originatorType = msg.getOriginator().getEntityType();
 | 
			
		||||
        EntityType originatorType = originator.getEntityType();
 | 
			
		||||
        switch (originatorType) {
 | 
			
		||||
            case TENANT:
 | 
			
		||||
                relationType = "Tenant";
 | 
			
		||||
@ -87,7 +77,7 @@ public class TbOriginatorTypeSwitchNode implements TbNode {
 | 
			
		||||
            default:
 | 
			
		||||
                throw new TbNodeException("Unsupported originator type: " + originatorType);
 | 
			
		||||
        }
 | 
			
		||||
        ctx.tellNext(msg, relationType);
 | 
			
		||||
        return relationType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,126 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 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.rule.engine.filter;
 | 
			
		||||
 | 
			
		||||
import org.junit.jupiter.api.AfterEach;
 | 
			
		||||
import org.junit.jupiter.api.BeforeEach;
 | 
			
		||||
import org.junit.jupiter.api.Test;
 | 
			
		||||
import org.mockito.ArgumentCaptor;
 | 
			
		||||
import org.thingsboard.common.util.JacksonUtil;
 | 
			
		||||
import org.thingsboard.rule.engine.api.EmptyNodeConfiguration;
 | 
			
		||||
import org.thingsboard.rule.engine.api.RuleEngineAssetProfileCache;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbContext;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNodeException;
 | 
			
		||||
import org.thingsboard.server.common.data.asset.AssetProfile;
 | 
			
		||||
import org.thingsboard.server.common.data.id.AssetId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.CustomerId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.EntityId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
import org.thingsboard.server.common.msg.TbMsg;
 | 
			
		||||
import org.thingsboard.server.common.msg.TbMsgMetaData;
 | 
			
		||||
import org.thingsboard.server.common.msg.queue.TbMsgCallback;
 | 
			
		||||
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
 | 
			
		||||
import static org.mockito.ArgumentMatchers.any;
 | 
			
		||||
import static org.mockito.ArgumentMatchers.eq;
 | 
			
		||||
import static org.mockito.Mockito.doReturn;
 | 
			
		||||
import static org.mockito.Mockito.mock;
 | 
			
		||||
import static org.mockito.Mockito.never;
 | 
			
		||||
import static org.mockito.Mockito.times;
 | 
			
		||||
import static org.mockito.Mockito.verify;
 | 
			
		||||
import static org.mockito.Mockito.when;
 | 
			
		||||
 | 
			
		||||
class TbAssetTypeSwitchNodeTest {
 | 
			
		||||
 | 
			
		||||
    TenantId tenantId;
 | 
			
		||||
    AssetId assetId;
 | 
			
		||||
    AssetId assetIdDeleted;
 | 
			
		||||
    AssetProfile assetProfile;
 | 
			
		||||
    TbContext ctx;
 | 
			
		||||
    TbAssetTypeSwitchNode node;
 | 
			
		||||
    EmptyNodeConfiguration config;
 | 
			
		||||
    TbMsgCallback callback;
 | 
			
		||||
    RuleEngineAssetProfileCache assetProfileCache;
 | 
			
		||||
 | 
			
		||||
    @BeforeEach
 | 
			
		||||
    void setUp() throws TbNodeException {
 | 
			
		||||
        tenantId = new TenantId(UUID.randomUUID());
 | 
			
		||||
        assetId = new AssetId(UUID.randomUUID());
 | 
			
		||||
        assetIdDeleted = new AssetId(UUID.randomUUID());
 | 
			
		||||
 | 
			
		||||
        assetProfile = new AssetProfile();
 | 
			
		||||
        assetProfile.setTenantId(tenantId);
 | 
			
		||||
        assetProfile.setName("TestAssetProfile");
 | 
			
		||||
 | 
			
		||||
        //node
 | 
			
		||||
        config = new EmptyNodeConfiguration();
 | 
			
		||||
        node = new TbAssetTypeSwitchNode();
 | 
			
		||||
        node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config)));
 | 
			
		||||
 | 
			
		||||
        //init mock
 | 
			
		||||
        ctx = mock(TbContext.class);
 | 
			
		||||
        assetProfileCache = mock(RuleEngineAssetProfileCache.class);
 | 
			
		||||
        callback = mock(TbMsgCallback.class);
 | 
			
		||||
 | 
			
		||||
        when(ctx.getTenantId()).thenReturn(tenantId);
 | 
			
		||||
        when(ctx.getAssetProfileCache()).thenReturn(assetProfileCache);
 | 
			
		||||
 | 
			
		||||
        doReturn(assetProfile).when(assetProfileCache).get(tenantId, assetId);
 | 
			
		||||
        doReturn(null).when(assetProfileCache).get(tenantId, assetIdDeleted);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @AfterEach
 | 
			
		||||
    void tearDown() {
 | 
			
		||||
        node.destroy();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    void givenMsg_whenOnMsg_then_Fail() {
 | 
			
		||||
        CustomerId customerId = new CustomerId(UUID.randomUUID());
 | 
			
		||||
        assertThatThrownBy(() -> {
 | 
			
		||||
            node.onMsg(ctx, getTbMsg(customerId));
 | 
			
		||||
        }).isInstanceOf(TbNodeException.class).hasMessageContaining("Unsupported originator type");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    void givenMsg_whenOnMsg_EntityIdDeleted_then_Fail() {
 | 
			
		||||
        assertThatThrownBy(() -> {
 | 
			
		||||
            node.onMsg(ctx, getTbMsg(assetIdDeleted));
 | 
			
		||||
        }).isInstanceOf(TbNodeException.class).hasMessageContaining("Asset profile for entity id");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    void givenMsg_whenOnMsg_then_Success() throws TbNodeException {
 | 
			
		||||
        TbMsg msg = getTbMsg(assetId);
 | 
			
		||||
        node.onMsg(ctx, msg);
 | 
			
		||||
 | 
			
		||||
        ArgumentCaptor<TbMsg> newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class);
 | 
			
		||||
        verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq("TestAssetProfile"));
 | 
			
		||||
        verify(ctx, never()).tellFailure(any(), any());
 | 
			
		||||
 | 
			
		||||
        TbMsg newMsg = newMsgCaptor.getValue();
 | 
			
		||||
        assertThat(newMsg).isNotNull();
 | 
			
		||||
        assertThat(newMsg).isSameAs(msg);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private TbMsg getTbMsg(EntityId entityId) {
 | 
			
		||||
        return TbMsg.newMsg("POST_ATTRIBUTES_REQUEST", entityId, new TbMsgMetaData(), "{}", callback);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,126 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 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.rule.engine.filter;
 | 
			
		||||
 | 
			
		||||
import org.junit.jupiter.api.AfterEach;
 | 
			
		||||
import org.junit.jupiter.api.BeforeEach;
 | 
			
		||||
import org.junit.jupiter.api.Test;
 | 
			
		||||
import org.mockito.ArgumentCaptor;
 | 
			
		||||
import org.thingsboard.common.util.JacksonUtil;
 | 
			
		||||
import org.thingsboard.rule.engine.api.EmptyNodeConfiguration;
 | 
			
		||||
import org.thingsboard.rule.engine.api.RuleEngineDeviceProfileCache;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbContext;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNodeException;
 | 
			
		||||
import org.thingsboard.server.common.data.DeviceProfile;
 | 
			
		||||
import org.thingsboard.server.common.data.id.CustomerId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.DeviceId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.EntityId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
import org.thingsboard.server.common.msg.TbMsg;
 | 
			
		||||
import org.thingsboard.server.common.msg.TbMsgMetaData;
 | 
			
		||||
import org.thingsboard.server.common.msg.queue.TbMsgCallback;
 | 
			
		||||
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
 | 
			
		||||
import static org.mockito.ArgumentMatchers.any;
 | 
			
		||||
import static org.mockito.ArgumentMatchers.eq;
 | 
			
		||||
import static org.mockito.Mockito.doReturn;
 | 
			
		||||
import static org.mockito.Mockito.mock;
 | 
			
		||||
import static org.mockito.Mockito.never;
 | 
			
		||||
import static org.mockito.Mockito.times;
 | 
			
		||||
import static org.mockito.Mockito.verify;
 | 
			
		||||
import static org.mockito.Mockito.when;
 | 
			
		||||
 | 
			
		||||
class TbDeviceTypeSwitchNodeTest {
 | 
			
		||||
 | 
			
		||||
    TenantId tenantId;
 | 
			
		||||
    DeviceId deviceId;
 | 
			
		||||
    DeviceId deviceIdDeleted;
 | 
			
		||||
    DeviceProfile deviceProfile;
 | 
			
		||||
    TbContext ctx;
 | 
			
		||||
    TbDeviceTypeSwitchNode node;
 | 
			
		||||
    EmptyNodeConfiguration config;
 | 
			
		||||
    TbMsgCallback callback;
 | 
			
		||||
    RuleEngineDeviceProfileCache deviceProfileCache;
 | 
			
		||||
 | 
			
		||||
    @BeforeEach
 | 
			
		||||
    void setUp() throws TbNodeException {
 | 
			
		||||
        tenantId = new TenantId(UUID.randomUUID());
 | 
			
		||||
        deviceId = new DeviceId(UUID.randomUUID());
 | 
			
		||||
        deviceIdDeleted = new DeviceId(UUID.randomUUID());
 | 
			
		||||
 | 
			
		||||
        deviceProfile = new DeviceProfile();
 | 
			
		||||
        deviceProfile.setTenantId(tenantId);
 | 
			
		||||
        deviceProfile.setName("TestDeviceProfile");
 | 
			
		||||
 | 
			
		||||
        //node
 | 
			
		||||
        config = new EmptyNodeConfiguration();
 | 
			
		||||
        node = new TbDeviceTypeSwitchNode();
 | 
			
		||||
        node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config)));
 | 
			
		||||
 | 
			
		||||
        //init mock
 | 
			
		||||
        ctx = mock(TbContext.class);
 | 
			
		||||
        deviceProfileCache = mock(RuleEngineDeviceProfileCache.class);
 | 
			
		||||
        callback = mock(TbMsgCallback.class);
 | 
			
		||||
 | 
			
		||||
        when(ctx.getTenantId()).thenReturn(tenantId);
 | 
			
		||||
        when(ctx.getDeviceProfileCache()).thenReturn(deviceProfileCache);
 | 
			
		||||
 | 
			
		||||
        doReturn(deviceProfile).when(deviceProfileCache).get(tenantId, deviceId);
 | 
			
		||||
        doReturn(null).when(deviceProfileCache).get(tenantId, deviceIdDeleted);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @AfterEach
 | 
			
		||||
    void tearDown() {
 | 
			
		||||
        node.destroy();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    void givenMsg_whenOnMsg_then_Fail() {
 | 
			
		||||
        CustomerId customerId = new CustomerId(UUID.randomUUID());
 | 
			
		||||
        assertThatThrownBy(() -> {
 | 
			
		||||
            node.onMsg(ctx, getTbMsg(customerId));
 | 
			
		||||
        }).isInstanceOf(TbNodeException.class).hasMessageContaining("Unsupported originator type");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    void givenMsg_whenOnMsg_EntityIdDeleted_then_Fail() {
 | 
			
		||||
        assertThatThrownBy(() -> {
 | 
			
		||||
            node.onMsg(ctx, getTbMsg(deviceIdDeleted));
 | 
			
		||||
        }).isInstanceOf(TbNodeException.class).hasMessageContaining("Device profile for entity id");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    void givenMsg_whenOnMsg_then_Success() throws TbNodeException {
 | 
			
		||||
        TbMsg msg = getTbMsg(deviceId);
 | 
			
		||||
        node.onMsg(ctx, msg);
 | 
			
		||||
 | 
			
		||||
        ArgumentCaptor<TbMsg> newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class);
 | 
			
		||||
        verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq("TestDeviceProfile"));
 | 
			
		||||
        verify(ctx, never()).tellFailure(any(), any());
 | 
			
		||||
 | 
			
		||||
        TbMsg newMsg = newMsgCaptor.getValue();
 | 
			
		||||
        assertThat(newMsg).isNotNull();
 | 
			
		||||
        assertThat(newMsg).isSameAs(msg);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private TbMsg getTbMsg(EntityId entityId) {
 | 
			
		||||
        return TbMsg.newMsg("POST_ATTRIBUTES_REQUEST", entityId, new TbMsgMetaData(), "{}", callback);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user