Tests added to validate DefaultRuleChains and EdgeDefaultRuleChains json templates

This commit is contained in:
Sergey Matvienko 2023-09-06 22:39:17 +02:00
parent 88f10881b2
commit eaa9a9b9cc
4 changed files with 136 additions and 20 deletions

View File

@ -28,7 +28,6 @@ import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
@ -104,7 +103,7 @@ public class InstallScripts {
@Autowired
private ResourceService resourceService;
private Path getTenantRuleChainsDir() {
Path getTenantRuleChainsDir() {
return Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, RULE_CHAINS_DIR);
}
@ -112,7 +111,7 @@ public class InstallScripts {
return Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, DEVICE_PROFILE_DIR, "rule_chain_template.json");
}
private Path getEdgeRuleChainsDir() {
Path getEdgeRuleChainsDir() {
return Paths.get(getDataDir(), JSON_DIR, EDGE_DIR, RULE_CHAINS_DIR);
}
@ -148,8 +147,8 @@ public class InstallScripts {
}
private void loadRuleChainsFromPath(TenantId tenantId, Path ruleChainsPath) throws IOException {
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(ruleChainsPath, path -> path.toString().endsWith(InstallScripts.JSON_EXT))) {
dirStream.forEach(
findRuleChainsFromPath(ruleChainsPath)
.forEach(
path -> {
try {
createRuleChainFromFile(tenantId, path, null);
@ -157,9 +156,15 @@ public class InstallScripts {
log.error("Unable to load rule chain from json: [{}]", path.toString());
throw new RuntimeException("Unable to load rule chain from json", e);
}
}
);
});
}
List<Path> findRuleChainsFromPath(Path ruleChainsPath) throws IOException {
List<Path> paths = new ArrayList<>();
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(ruleChainsPath, path -> path.toString().endsWith(InstallScripts.JSON_EXT))) {
dirStream.forEach(paths::add);
}
return paths;
}
public RuleChain createDefaultRuleChain(TenantId tenantId, String ruleChainName) throws IOException {

View File

@ -0,0 +1,111 @@
/**
* Copyright © 2016-2023 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.install;
import com.fasterxml.jackson.databind.JsonNode;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService;
import org.thingsboard.server.dao.resource.ResourceService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.service.validator.RuleChainDataValidator;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.usagerecord.ApiLimitService;
import org.thingsboard.server.dao.widget.WidgetTypeService;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.willReturn;
@SpringBootTest(classes = {InstallScripts.class, RuleChainDataValidator.class})
class InstallScriptsTest {
@MockBean
RuleChainService ruleChainService;
@MockBean
DashboardService dashboardService;
@MockBean
WidgetTypeService widgetTypeService;
@MockBean
WidgetsBundleService widgetsBundleService;
@MockBean
OAuth2ConfigTemplateService oAuth2TemplateService;
@MockBean
ResourceService resourceService;
@SpyBean
InstallScripts installScripts;
@MockBean
TenantService tenantService;
@MockBean
ApiLimitService apiLimitService;
@SpyBean
RuleChainDataValidator ruleChainValidator;
TenantId tenantId = TenantId.fromUUID(UUID.fromString("9ef79cdf-37a8-4119-b682-2e7ed4e018da"));
@BeforeEach
void setUp() {
willReturn(true).given(tenantService).tenantExists(tenantId);
willReturn(true).given(apiLimitService).checkEntitiesLimit(any(), any());
}
@Test
void testDefaultRuleChainsTemplates() throws IOException {
Path tenantRuleChainsDir = installScripts.getTenantRuleChainsDir();
List<Path> ruleChainsFromPath = installScripts.findRuleChainsFromPath(tenantRuleChainsDir);
ruleChainsFromPath.forEach(this::validateRuleChainTemplate);
}
@Test
void testDefaultEdgeRuleChainsTemplates() throws IOException {
Path edgeChainsDir = installScripts.getEdgeRuleChainsDir();
List<Path> ruleChainsFromPath = installScripts.findRuleChainsFromPath(edgeChainsDir);
ruleChainsFromPath.forEach(this::validateRuleChainTemplate);
}
private void validateRuleChainTemplate(Path templateFilePath) {
JsonNode ruleChainJson = JacksonUtil.toJsonNode(templateFilePath.toFile());
RuleChain ruleChain = JacksonUtil.treeToValue(ruleChainJson.get("ruleChain"), RuleChain.class);
ruleChain.setTenantId(tenantId);
ruleChainValidator.validate(ruleChain, RuleChain::getTenantId);
ruleChain.setId(new RuleChainId(UUID.randomUUID()));
RuleChainMetaData ruleChainMetaData = JacksonUtil.treeToValue(ruleChainJson.get("metadata"), RuleChainMetaData.class);
ruleChainMetaData.setRuleChainId(ruleChain.getId());
List<Throwable> throwables = RuleChainDataValidator.validateMetaData(ruleChainMetaData);
assertThat(throwables).as("templateFilePath " + templateFilePath)
.containsExactlyInAnyOrderElementsOf(Collections.emptyList());
}
}

View File

@ -31,7 +31,6 @@ import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.common.data.util.ReflectionUtils;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.rule.RuleChainDao;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.service.ConstraintValidator;
import org.thingsboard.server.dao.service.DataValidator;
@ -41,15 +40,14 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
@Component
@Slf4j
public class RuleChainDataValidator extends DataValidator<RuleChain> {
@Autowired
private RuleChainDao ruleChainDao;
@Autowired
@Lazy
private RuleChainService ruleChainService;
@ -88,15 +86,19 @@ public class RuleChainDataValidator extends DataValidator<RuleChain> {
}
}
public static void validateMetaData(RuleChainMetaData ruleChainMetaData) {
public static List<Throwable> validateMetaData(RuleChainMetaData ruleChainMetaData) {
ConstraintValidator.validateFields(ruleChainMetaData);
ruleChainMetaData.getNodes().forEach(RuleChainDataValidator::validateRuleNode);
List<Throwable> throwables = ruleChainMetaData.getNodes().stream()
.map(RuleChainDataValidator::validateRuleNode)
.filter(Objects::nonNull)
.collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(ruleChainMetaData.getConnections())) {
validateCircles(ruleChainMetaData.getConnections());
}
return throwables;
}
public static void validateRuleNode(RuleNode ruleNode) {
public static Throwable validateRuleNode(RuleNode ruleNode) {
String errorPrefix = "'" + ruleNode.getName() + "' node configuration is invalid: ";
ConstraintValidator.validateFields(ruleNode, errorPrefix);
Object nodeConfig;
@ -104,11 +106,12 @@ public class RuleChainDataValidator extends DataValidator<RuleChain> {
Class<Object> nodeConfigType = ReflectionUtils.getAnnotationProperty(ruleNode.getType(),
"org.thingsboard.rule.engine.api.RuleNode", "configClazz");
nodeConfig = JacksonUtil.treeToValue(ruleNode.getConfiguration(), nodeConfigType);
} catch (Exception e) {
log.warn("Failed to validate node configuration: {}", ExceptionUtils.getRootCauseMessage(e));
return;
} catch (Throwable t) {
log.warn("Failed to validate node configuration: {}", ExceptionUtils.getRootCauseMessage(t));
return t;
}
ConstraintValidator.validateFields(nodeConfig, errorPrefix);
return null;
}
private static void validateCircles(List<NodeConnectionInfo> connectionInfos) {

View File

@ -23,7 +23,6 @@ import org.springframework.boot.test.mock.mockito.SpyBean;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.dao.rule.RuleChainDao;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.tenant.TenantService;
@ -35,8 +34,6 @@ import static org.mockito.Mockito.verify;
@SpringBootTest(classes = RuleChainDataValidator.class)
class RuleChainDataValidatorTest {
@MockBean
RuleChainDao ruleChainDao;
@MockBean
RuleChainService ruleChainService;
@MockBean