Merge branch 'develop/3.4' into refactoring_test_customer_05
# Conflicts: # common/cache/src/test/java/org/thingsboard/server/cache/CacheSpecsMapTest.java
This commit is contained in:
commit
439fe05a7e
@ -28,6 +28,10 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.data.audit.ActionType;
|
||||
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
|
||||
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
|
||||
import org.thingsboard.server.common.data.exception.ThingsboardException;
|
||||
import org.thingsboard.server.common.data.id.EntityId;
|
||||
import org.thingsboard.server.common.data.id.EntityIdFactory;
|
||||
@ -37,9 +41,11 @@ import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
|
||||
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
|
||||
import org.thingsboard.server.queue.util.TbCoreComponent;
|
||||
import org.thingsboard.server.service.entitiy.entityRelation.TbEntityRelationService;
|
||||
import org.thingsboard.server.service.security.model.SecurityUser;
|
||||
import org.thingsboard.server.service.security.permission.Operation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.thingsboard.server.controller.ControllerConstants.ENTITY_ID_PARAM_DESCRIPTION;
|
||||
@ -80,8 +86,8 @@ public class EntityRelationController extends BaseController {
|
||||
public void saveRelation(@ApiParam(value = "A JSON value representing the relation.", required = true)
|
||||
@RequestBody EntityRelation relation) throws ThingsboardException {
|
||||
checkNotNull(relation);
|
||||
checkEntityId(relation.getFrom(), Operation.WRITE);
|
||||
checkEntityId(relation.getTo(), Operation.WRITE);
|
||||
checkCanCreateRelation(relation.getFrom());
|
||||
checkCanCreateRelation(relation.getTo());
|
||||
if (relation.getTypeGroup() == null) {
|
||||
relation.setTypeGroup(RelationTypeGroup.COMMON);
|
||||
}
|
||||
@ -107,8 +113,9 @@ public class EntityRelationController extends BaseController {
|
||||
checkParameter(TO_TYPE, strToType);
|
||||
EntityId fromId = EntityIdFactory.getByTypeAndId(strFromType, strFromId);
|
||||
EntityId toId = EntityIdFactory.getByTypeAndId(strToType, strToId);
|
||||
checkEntityId(fromId, Operation.WRITE);
|
||||
checkEntityId(toId, Operation.WRITE);
|
||||
checkCanCreateRelation(fromId);
|
||||
checkCanCreateRelation(toId);
|
||||
|
||||
RelationTypeGroup relationTypeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON);
|
||||
EntityRelation relation = new EntityRelation(fromId, toId, strRelationType, relationTypeGroup);
|
||||
|
||||
@ -341,6 +348,14 @@ public class EntityRelationController extends BaseController {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkCanCreateRelation(EntityId entityId) throws ThingsboardException {
|
||||
SecurityUser currentUser = getCurrentUser();
|
||||
var isTenantAdminAndRelateToSelf = currentUser.isTenantAdmin() && currentUser.getTenantId().equals(entityId);
|
||||
if (!isTenantAdminAndRelateToSelf) {
|
||||
checkEntityId(entityId, Operation.WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends EntityRelation> List<T> filterRelationsByReadPermission(List<T> relationsByQuery) {
|
||||
return relationsByQuery.stream().filter(relationByQuery -> {
|
||||
try {
|
||||
|
||||
@ -0,0 +1,439 @@
|
||||
/**
|
||||
* 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.controller;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.server.common.data.Device;
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.data.EntityView;
|
||||
import org.thingsboard.server.common.data.Tenant;
|
||||
import org.thingsboard.server.common.data.User;
|
||||
import org.thingsboard.server.common.data.relation.EntityRelation;
|
||||
import org.thingsboard.server.common.data.relation.EntityRelationInfo;
|
||||
import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
|
||||
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
|
||||
import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter;
|
||||
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
|
||||
import org.thingsboard.server.common.data.relation.RelationsSearchParameters;
|
||||
import org.thingsboard.server.common.data.security.Authority;
|
||||
import org.thingsboard.server.dao.relation.RelationService;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@Slf4j
|
||||
public class BaseEntityRelationControllerTest extends AbstractControllerTest {
|
||||
|
||||
public static final String BASE_DEVICE_NAME = "Test dummy device";
|
||||
|
||||
@Autowired
|
||||
RelationService relationService;
|
||||
|
||||
private IdComparator<EntityView> idComparator;
|
||||
private Tenant savedTenant;
|
||||
private User tenantAdmin;
|
||||
private Device mainDevice;
|
||||
|
||||
@Before
|
||||
public void beforeTest() throws Exception {
|
||||
loginSysAdmin();
|
||||
idComparator = new IdComparator<>();
|
||||
|
||||
Tenant tenant = new Tenant();
|
||||
tenant.setTitle("Test tenant");
|
||||
|
||||
savedTenant = doPost("/api/tenant", tenant, Tenant.class);
|
||||
Assert.assertNotNull(savedTenant);
|
||||
|
||||
tenantAdmin = new User();
|
||||
tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
|
||||
tenantAdmin.setTenantId(savedTenant.getId());
|
||||
tenantAdmin.setEmail("tenant2@thingsboard.org");
|
||||
tenantAdmin.setFirstName("Joe");
|
||||
tenantAdmin.setLastName("Downs");
|
||||
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
|
||||
|
||||
Device device = new Device();
|
||||
device.setName("Main test device");
|
||||
device.setType("default");
|
||||
mainDevice = doPost("/api/device", device, Device.class);
|
||||
}
|
||||
|
||||
@After
|
||||
public void afterTest() throws Exception {
|
||||
loginSysAdmin();
|
||||
|
||||
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveAndFindRelation() throws Exception {
|
||||
Device device = buildSimpleDevice("Test device 1");
|
||||
|
||||
EntityRelation relation = createFromRelation(mainDevice, device, "CONTAINS");
|
||||
doPost("/api/relation", relation).andExpect(status().isOk());
|
||||
|
||||
String url = String.format("/api/relation?fromId=%s&fromType=%s&relationType=%s&toId=%s&toType=%s",
|
||||
mainDevice.getUuidId(), EntityType.DEVICE,
|
||||
"CONTAINS", device.getUuidId(), EntityType.DEVICE
|
||||
);
|
||||
|
||||
EntityRelation foundRelation = doGet(url, EntityRelation.class);
|
||||
|
||||
Assert.assertNotNull("Relation is not found!", foundRelation);
|
||||
Assert.assertEquals("Found relation is not equals origin!", relation, foundRelation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveAndFindRelationsByFrom() throws Exception {
|
||||
final int numOfDevices = 30;
|
||||
createDevicesByFrom(numOfDevices, BASE_DEVICE_NAME);
|
||||
String url = String.format("/api/relations?fromId=%s&fromType=%s",
|
||||
mainDevice.getUuidId(), EntityType.DEVICE
|
||||
);
|
||||
|
||||
assertFoundList(url, numOfDevices);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveAndFindRelationsByTo() throws Exception {
|
||||
final int numOfDevices = 30;
|
||||
createDevicesByTo(numOfDevices, BASE_DEVICE_NAME);
|
||||
String url = String.format("/api/relations?toId=%s&toType=%s",
|
||||
mainDevice.getUuidId(), EntityType.DEVICE
|
||||
);
|
||||
|
||||
assertFoundList(url, numOfDevices);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveAndFindRelationsByFromWithRelationType() throws Exception {
|
||||
final int numOfDevices = 30;
|
||||
createDevicesByFrom(numOfDevices, BASE_DEVICE_NAME);
|
||||
|
||||
Device device = buildSimpleDevice("Unique dummy test device ");
|
||||
EntityRelation relation = createFromRelation(mainDevice, device, "TEST");
|
||||
|
||||
doPost("/api/relation", relation).andExpect(status().isOk());
|
||||
String url = String.format("/api/relations?fromId=%s&fromType=%s&relationType=%s",
|
||||
mainDevice.getUuidId(), EntityType.DEVICE, "TEST"
|
||||
);
|
||||
|
||||
assertFoundList(url, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveAndFindRelationsByToWithRelationType() throws Exception {
|
||||
final int numOfDevices = 30;
|
||||
createDevicesByFrom(numOfDevices, BASE_DEVICE_NAME);
|
||||
|
||||
Device device = buildSimpleDevice("Unique dummy test device ");
|
||||
EntityRelation relation = createFromRelation(device, mainDevice, "TEST");
|
||||
|
||||
doPost("/api/relation", relation).andExpect(status().isOk());
|
||||
String url = String.format("/api/relations?toId=%s&toType=%s&relationType=%s",
|
||||
mainDevice.getUuidId(), EntityType.DEVICE, "TEST"
|
||||
);
|
||||
|
||||
assertFoundList(url, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindRelationsInfoByFrom() throws Exception {
|
||||
final int numOfDevices = 30;
|
||||
createDevicesByFrom(numOfDevices, BASE_DEVICE_NAME);
|
||||
String url = String.format("/api/relations/info?fromId=%s&fromType=%s",
|
||||
mainDevice.getUuidId(), EntityType.DEVICE
|
||||
);
|
||||
|
||||
List<EntityRelationInfo> relationsInfos =
|
||||
JacksonUtil.convertValue(doGet(url, JsonNode.class), new TypeReference<List<EntityRelationInfo>>() {
|
||||
});
|
||||
|
||||
Assert.assertNotNull("Relations is not found!", relationsInfos);
|
||||
Assert.assertEquals("List of found relationsInfos is not equal to number of created relations!",
|
||||
numOfDevices, relationsInfos.size());
|
||||
|
||||
assertRelationsInfosByFrom(relationsInfos);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindRelationsInfoByTo() throws Exception {
|
||||
final int numOfDevices = 30;
|
||||
createDevicesByTo(numOfDevices, BASE_DEVICE_NAME);
|
||||
String url = String.format("/api/relations/info?toId=%s&toType=%s",
|
||||
mainDevice.getUuidId(), EntityType.DEVICE
|
||||
);
|
||||
|
||||
List<EntityRelationInfo> relationsInfos =
|
||||
JacksonUtil.convertValue(doGet(url, JsonNode.class), new TypeReference<List<EntityRelationInfo>>() {
|
||||
});
|
||||
|
||||
Assert.assertNotNull("Relations is not found!", relationsInfos);
|
||||
Assert.assertEquals("List of found relationsInfos is not equal to number of created relations!",
|
||||
numOfDevices, relationsInfos.size());
|
||||
|
||||
assertRelationsInfosByTo(relationsInfos);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteRelation() throws Exception {
|
||||
Device device = buildSimpleDevice("Test device 1");
|
||||
|
||||
EntityRelation relation = createFromRelation(mainDevice, device, "CONTAINS");
|
||||
doPost("/api/relation", relation).andExpect(status().isOk());
|
||||
|
||||
String url = String.format("/api/relation?fromId=%s&fromType=%s&relationType=%s&toId=%s&toType=%s",
|
||||
mainDevice.getUuidId(), EntityType.DEVICE,
|
||||
"CONTAINS", device.getUuidId(), EntityType.DEVICE
|
||||
);
|
||||
|
||||
EntityRelation foundRelation = doGet(url, EntityRelation.class);
|
||||
|
||||
Assert.assertNotNull("Relation is not found!", foundRelation);
|
||||
Assert.assertEquals("Found relation is not equals origin!", relation, foundRelation);
|
||||
|
||||
doDelete(url).andExpect(status().isOk());
|
||||
doGet(url).andExpect(status().is4xxClientError());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteRelations() throws Exception {
|
||||
final int numOfDevices = 30;
|
||||
createDevicesByFrom(numOfDevices, BASE_DEVICE_NAME + " from");
|
||||
createDevicesByTo(numOfDevices, BASE_DEVICE_NAME + " to");
|
||||
|
||||
String urlTo = String.format("/api/relations?toId=%s&toType=%s",
|
||||
mainDevice.getUuidId(), EntityType.DEVICE
|
||||
);
|
||||
String urlFrom = String.format("/api/relations?fromId=%s&fromType=%s",
|
||||
mainDevice.getUuidId(), EntityType.DEVICE
|
||||
);
|
||||
|
||||
assertFoundList(urlTo, numOfDevices);
|
||||
assertFoundList(urlFrom, numOfDevices);
|
||||
|
||||
String url = String.format("/api/relations?entityId=%s&entityType=%s",
|
||||
mainDevice.getUuidId(), EntityType.DEVICE
|
||||
);
|
||||
doDelete(url).andExpect(status().isOk());
|
||||
|
||||
Assert.assertTrue(
|
||||
"Performed deletion of all relations but some relations were found!",
|
||||
doGet(urlTo, List.class).isEmpty()
|
||||
);
|
||||
Assert.assertTrue(
|
||||
"Performed deletion of all relations but some relations were found!",
|
||||
doGet(urlFrom, List.class).isEmpty()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindRelationsByFromQuery() throws Exception {
|
||||
final int numOfDevices = 30;
|
||||
createDevicesByFrom(numOfDevices, BASE_DEVICE_NAME);
|
||||
|
||||
EntityRelationsQuery query = new EntityRelationsQuery();
|
||||
query.setParameters(new RelationsSearchParameters(
|
||||
mainDevice.getUuidId(), EntityType.DEVICE,
|
||||
EntitySearchDirection.FROM,
|
||||
RelationTypeGroup.COMMON,
|
||||
1, true
|
||||
));
|
||||
query.setFilters(Collections.singletonList(
|
||||
new RelationEntityTypeFilter("CONTAINS", List.of(EntityType.DEVICE))
|
||||
));
|
||||
|
||||
List<EntityRelation> relations = readResponse(
|
||||
doPost("/api/relations", query).andExpect(status().isOk()),
|
||||
new TypeReference<List<EntityRelation>>() {}
|
||||
);
|
||||
|
||||
assertFoundRelations(relations, numOfDevices);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindRelationsByToQuery() throws Exception {
|
||||
final int numOfDevices = 30;
|
||||
createDevicesByTo(numOfDevices, BASE_DEVICE_NAME);
|
||||
|
||||
EntityRelationsQuery query = new EntityRelationsQuery();
|
||||
query.setParameters(new RelationsSearchParameters(
|
||||
mainDevice.getUuidId(), EntityType.DEVICE,
|
||||
EntitySearchDirection.TO,
|
||||
RelationTypeGroup.COMMON,
|
||||
1, true
|
||||
));
|
||||
query.setFilters(Collections.singletonList(
|
||||
new RelationEntityTypeFilter("CONTAINS", List.of(EntityType.DEVICE))
|
||||
));
|
||||
|
||||
List<EntityRelation> relations = readResponse(
|
||||
doPost("/api/relations", query).andExpect(status().isOk()),
|
||||
new TypeReference<List<EntityRelation>>() {}
|
||||
);
|
||||
|
||||
assertFoundRelations(relations, numOfDevices);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindRelationsInfoByFromQuery() throws Exception{
|
||||
final int numOfDevices = 30;
|
||||
createDevicesByFrom(numOfDevices, BASE_DEVICE_NAME);
|
||||
|
||||
EntityRelationsQuery query = new EntityRelationsQuery();
|
||||
query.setParameters(new RelationsSearchParameters(
|
||||
mainDevice.getUuidId(), EntityType.DEVICE,
|
||||
EntitySearchDirection.FROM,
|
||||
RelationTypeGroup.COMMON,
|
||||
1, true
|
||||
));
|
||||
query.setFilters(Collections.singletonList(
|
||||
new RelationEntityTypeFilter("CONTAINS", List.of(EntityType.DEVICE))
|
||||
));
|
||||
|
||||
List<EntityRelationInfo> relationsInfo = readResponse(
|
||||
doPost("/api/relations/info", query).andExpect(status().isOk()),
|
||||
new TypeReference<List<EntityRelationInfo>>() {}
|
||||
);
|
||||
|
||||
assertRelationsInfosByFrom(relationsInfo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindRelationsInfoByToQuery() throws Exception{
|
||||
final int numOfDevices = 30;
|
||||
createDevicesByTo(numOfDevices, BASE_DEVICE_NAME);
|
||||
|
||||
EntityRelationsQuery query = new EntityRelationsQuery();
|
||||
query.setParameters(new RelationsSearchParameters(
|
||||
mainDevice.getUuidId(), EntityType.DEVICE,
|
||||
EntitySearchDirection.TO,
|
||||
RelationTypeGroup.COMMON,
|
||||
1, true
|
||||
));
|
||||
query.setFilters(Collections.singletonList(
|
||||
new RelationEntityTypeFilter("CONTAINS", List.of(EntityType.DEVICE))
|
||||
));
|
||||
|
||||
List<EntityRelationInfo> relationsInfo = readResponse(
|
||||
doPost("/api/relations/info", query).andExpect(status().isOk()),
|
||||
new TypeReference<List<EntityRelationInfo>>() {}
|
||||
);
|
||||
|
||||
assertRelationsInfosByTo(relationsInfo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRelationFromTenantToDevice() throws Exception{
|
||||
EntityRelation relation = new EntityRelation(tenantAdmin.getTenantId(), mainDevice.getId(), "CONTAINS");
|
||||
doPost("/api/relation", relation).andExpect(status().isOk());
|
||||
|
||||
String url = String.format("/api/relation?fromId=%s&fromType=%s&relationType=%s&toId=%s&toType=%s",
|
||||
tenantAdmin.getTenantId(), EntityType.TENANT,
|
||||
"CONTAINS", mainDevice.getUuidId(), EntityType.DEVICE
|
||||
);
|
||||
|
||||
EntityRelation foundRelation = doGet(url, EntityRelation.class);
|
||||
|
||||
Assert.assertNotNull("Relation is not found!", foundRelation);
|
||||
Assert.assertEquals("Found relation is not equals origin!", relation, foundRelation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRelationFromDeviceToTenant() throws Exception{
|
||||
EntityRelation relation = new EntityRelation(mainDevice.getId(), tenantAdmin.getTenantId(), "CONTAINS");
|
||||
doPost("/api/relation", relation).andExpect(status().isOk());
|
||||
|
||||
String url = String.format("/api/relation?fromId=%s&fromType=%s&relationType=%s&toId=%s&toType=%s",
|
||||
mainDevice.getUuidId(), EntityType.DEVICE,
|
||||
"CONTAINS", tenantAdmin.getTenantId(), EntityType.TENANT
|
||||
);
|
||||
|
||||
EntityRelation foundRelation = doGet(url, EntityRelation.class);
|
||||
|
||||
Assert.assertNotNull("Relation is not found!", foundRelation);
|
||||
Assert.assertEquals("Found relation is not equals origin!", relation, foundRelation);
|
||||
}
|
||||
|
||||
private Device buildSimpleDevice(String name) throws Exception {
|
||||
Device device = new Device();
|
||||
device.setName(name);
|
||||
device.setType("default");
|
||||
device = doPost("/api/device", device, Device.class);
|
||||
return device;
|
||||
}
|
||||
|
||||
private EntityRelation createFromRelation(Device mainDevice, Device device, String relationType) {
|
||||
return new EntityRelation(mainDevice.getId(), device.getId(), relationType);
|
||||
}
|
||||
|
||||
private void createDevicesByFrom(int numOfDevices, String baseName) throws Exception {
|
||||
for (int i = 0; i < numOfDevices; i++) {
|
||||
Device device = buildSimpleDevice(baseName + i);
|
||||
|
||||
EntityRelation relation = createFromRelation(mainDevice, device, "CONTAINS");
|
||||
doPost("/api/relation", relation).andExpect(status().isOk());
|
||||
}
|
||||
}
|
||||
|
||||
private void createDevicesByTo(int numOfDevices, String baseName) throws Exception {
|
||||
for (int i = 0; i < numOfDevices; i++) {
|
||||
Device device = buildSimpleDevice(baseName + i);
|
||||
EntityRelation relation = createFromRelation(device, mainDevice, "CONTAINS");
|
||||
doPost("/api/relation", relation).andExpect(status().isOk());
|
||||
}
|
||||
}
|
||||
|
||||
private void assertFoundRelations(List<EntityRelation> relations, int numOfDevices) {
|
||||
Assert.assertNotNull("Relations is not found!", relations);
|
||||
Assert.assertEquals("List of found relations is not equal to number of created relations!",
|
||||
numOfDevices, relations.size());
|
||||
}
|
||||
|
||||
private void assertFoundList(String url, int numOfDevices) throws Exception {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<EntityRelation> relations = doGet(url, List.class);
|
||||
assertFoundRelations(relations, numOfDevices);
|
||||
}
|
||||
|
||||
private void assertRelationsInfosByFrom(List<EntityRelationInfo> relationsInfos) {
|
||||
for (EntityRelationInfo info : relationsInfos) {
|
||||
Assert.assertEquals("Wrong FROM entityId!", mainDevice.getId(), info.getFrom());
|
||||
Assert.assertTrue("Wrong FROM name!", info.getToName().contains(BASE_DEVICE_NAME));
|
||||
Assert.assertEquals("Wrong relationType!", "CONTAINS", info.getType());
|
||||
}
|
||||
}
|
||||
|
||||
private void assertRelationsInfosByTo(List<EntityRelationInfo> relationsInfos) {
|
||||
for (EntityRelationInfo info : relationsInfos) {
|
||||
Assert.assertEquals("Wrong TO entityId!", mainDevice.getId(), info.getTo());
|
||||
Assert.assertTrue("Wrong TO name!", info.getFromName().contains(BASE_DEVICE_NAME));
|
||||
Assert.assertEquals("Wrong relationType!", "CONTAINS", info.getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* 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.controller.sql;
|
||||
|
||||
import org.thingsboard.server.controller.BaseEntityRelationControllerTest;
|
||||
import org.thingsboard.server.dao.service.DaoSqlTest;
|
||||
|
||||
@DaoSqlTest
|
||||
public class EntityRelationControllerSqlTest extends BaseEntityRelationControllerTest {
|
||||
}
|
||||
@ -45,12 +45,14 @@ public class CacheSpecsMapTest {
|
||||
CacheManager cacheManager;
|
||||
|
||||
@Test
|
||||
public void verifyTransactionAwareCacheManagerProxy() {
|
||||
public void verifyNotTransactionAwareCacheManagerProxy() {
|
||||
// We no longer use built-in transaction support for the caches, because we have our own cache cleanup and transaction logic that implements CAS.
|
||||
assertThat(cacheManager).isInstanceOf(SimpleCacheManager.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenCacheConfig_whenCacheManagerReady_thenVerifyExistedCachesWithTransactionAwareCacheDecorator() {
|
||||
public void givenCacheConfig_whenCacheManagerReady_thenVerifyExistedCachesWithNoTransactionAwareCacheDecorator() {
|
||||
// We no longer use built-in transaction support for the caches, because we have our own cache cleanup and transaction logic that implements CAS.
|
||||
assertThat(cacheManager.getCache("relations")).isInstanceOf(CaffeineCache.class);
|
||||
assertThat(cacheManager.getCache("devices")).isInstanceOf(CaffeineCache.class);
|
||||
}
|
||||
|
||||
13
pom.xml
13
pom.xml
@ -128,6 +128,7 @@
|
||||
<java-websocket.version>1.5.2</java-websocket.version>
|
||||
<jupiter.version>5.8.2</jupiter.version> <!-- keep the same version as spring-boot-starter-test depend on jupiter-->
|
||||
<json-path.version>2.6.0</json-path.version>
|
||||
<mock-server.version>5.13.1</mock-server.version>
|
||||
<spring-test-dbunit.version>1.3.0</spring-test-dbunit.version> <!-- 2016 -->
|
||||
<takari-cpsuite.version>1.2.7</takari-cpsuite.version> <!-- 2015 -->
|
||||
<!-- BLACKBOX TEST SCOPE -->
|
||||
@ -1877,6 +1878,18 @@
|
||||
<version>${zeroturnaround.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mock-server</groupId>
|
||||
<artifactId>mockserver-netty</artifactId>
|
||||
<version>${mock-server.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mock-server</groupId>
|
||||
<artifactId>mockserver-client-java</artifactId>
|
||||
<version>${mock-server.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.opensmpp</groupId>
|
||||
<artifactId>opensmpp-core</artifactId>
|
||||
|
||||
@ -136,6 +136,15 @@
|
||||
<artifactId>awaitility</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mock-server</groupId>
|
||||
<artifactId>mockserver-netty</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mock-server</groupId>
|
||||
<artifactId>mockserver-client-java</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.cassandraunit</groupId>
|
||||
<artifactId>cassandra-unit</artifactId>
|
||||
|
||||
@ -40,6 +40,7 @@ import org.springframework.util.concurrent.ListenableFuture;
|
||||
import org.springframework.util.concurrent.ListenableFutureCallback;
|
||||
import org.springframework.web.client.AsyncRestTemplate;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
import org.thingsboard.rule.engine.api.TbContext;
|
||||
import org.thingsboard.rule.engine.api.TbNodeException;
|
||||
import org.thingsboard.rule.engine.api.TbRelationTypes;
|
||||
@ -54,6 +55,7 @@ import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLException;
|
||||
import java.net.Authenticator;
|
||||
import java.net.PasswordAuthentication;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Deque;
|
||||
@ -189,8 +191,9 @@ public class TbHttpClient {
|
||||
entity = new HttpEntity<>(msg.getData(), headers);
|
||||
}
|
||||
|
||||
URI uri = buildEncodedUri(endpointUrl);
|
||||
ListenableFuture<ResponseEntity<String>> future = httpClient.exchange(
|
||||
endpointUrl, method, entity, String.class);
|
||||
uri, method, entity, String.class);
|
||||
future.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() {
|
||||
@Override
|
||||
public void onFailure(Throwable throwable) {
|
||||
@ -214,6 +217,28 @@ public class TbHttpClient {
|
||||
}
|
||||
}
|
||||
|
||||
public URI buildEncodedUri(String endpointUrl) {
|
||||
if (endpointUrl == null) {
|
||||
throw new RuntimeException("Url string cannot be null!");
|
||||
}
|
||||
if (endpointUrl.isEmpty()) {
|
||||
throw new RuntimeException("Url string cannot be empty!");
|
||||
}
|
||||
|
||||
URI uri = UriComponentsBuilder.fromUriString(endpointUrl).build().encode().toUri();
|
||||
if (uri.getScheme() == null || uri.getScheme().isEmpty()) {
|
||||
throw new RuntimeException("Transport scheme(protocol) must be provided!");
|
||||
}
|
||||
|
||||
boolean authorityNotValid = uri.getAuthority() == null || uri.getAuthority().isEmpty();
|
||||
boolean hostNotValid = uri.getHost() == null || uri.getHost().isEmpty();
|
||||
if (authorityNotValid || hostNotValid) {
|
||||
throw new RuntimeException("Url string is invalid!");
|
||||
}
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
private TbMsg processResponse(TbContext ctx, TbMsg origMsg, ResponseEntity<String> response) {
|
||||
TbMsgMetaData metaData = origMsg.getMetaData();
|
||||
metaData.putValue(STATUS, response.getStatusCode().name());
|
||||
|
||||
@ -18,16 +18,38 @@ package org.thingsboard.rule.engine.rest;
|
||||
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import org.awaitility.Awaitility;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockserver.integration.ClientAndServer;
|
||||
import org.springframework.web.client.AsyncRestTemplate;
|
||||
import org.thingsboard.rule.engine.api.TbContext;
|
||||
import org.thingsboard.server.common.data.id.DeviceId;
|
||||
import org.thingsboard.server.common.data.id.EntityId;
|
||||
import org.thingsboard.server.common.msg.TbMsg;
|
||||
import org.thingsboard.server.common.msg.TbMsgMetaData;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.BDDMockito.willCallRealMethod;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockserver.integration.ClientAndServer.startClientAndServer;
|
||||
import static org.mockserver.model.HttpRequest.request;
|
||||
import static org.mockserver.model.HttpResponse.response;
|
||||
|
||||
public class TbHttpClientTest {
|
||||
|
||||
@ -58,4 +80,125 @@ public class TbHttpClientTest {
|
||||
eventLoop = client.getSharedOrCreateEventLoopGroup(null);
|
||||
assertThat(eventLoop, instanceOf(NioEventLoopGroup.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildSimpleUri() {
|
||||
Mockito.when(client.buildEncodedUri(any())).thenCallRealMethod();
|
||||
String url = "http://localhost:8080/";
|
||||
URI uri = client.buildEncodedUri(url);
|
||||
Assert.assertEquals(url, uri.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildUriWithoutProtocol() {
|
||||
Mockito.when(client.buildEncodedUri(any())).thenCallRealMethod();
|
||||
String url = "localhost:8080/";
|
||||
assertThatThrownBy(() -> client.buildEncodedUri(url));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildInvalidUri() {
|
||||
Mockito.when(client.buildEncodedUri(any())).thenCallRealMethod();
|
||||
String url = "aaa";
|
||||
assertThatThrownBy(() -> client.buildEncodedUri(url));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildUriWithSpecialSymbols() {
|
||||
Mockito.when(client.buildEncodedUri(any())).thenCallRealMethod();
|
||||
String url = "http://192.168.1.1/data?d={\"a\": 12}";
|
||||
String expected = "http://192.168.1.1/data?d=%7B%22a%22:%2012%7D";
|
||||
URI uri = client.buildEncodedUri(url);
|
||||
Assert.assertEquals(expected, uri.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessMessageWithJsonInUrlVariable() throws Exception {
|
||||
String host = "localhost";
|
||||
String path = "/api";
|
||||
String paramKey = "data";
|
||||
String paramVal = "[{\"test\":\"test\"}]";
|
||||
String successResponseBody = "SUCCESS";
|
||||
|
||||
var server = setUpDummyServer(host, path, paramKey, paramVal, successResponseBody);
|
||||
|
||||
String endpointUrl = String.format(
|
||||
"http://%s:%d%s?%s=%s",
|
||||
host, server.getPort(), path, paramKey, paramVal
|
||||
);
|
||||
String method = "GET";
|
||||
|
||||
|
||||
var config = new TbRestApiCallNodeConfiguration()
|
||||
.defaultConfiguration();
|
||||
config.setRequestMethod(method);
|
||||
config.setRestEndpointUrlPattern(endpointUrl);
|
||||
config.setUseSimpleClientHttpFactory(true);
|
||||
|
||||
var asyncRestTemplate = new AsyncRestTemplate();
|
||||
|
||||
var httpClient = new TbHttpClient(config, eventLoop);
|
||||
httpClient.setHttpClient(asyncRestTemplate);
|
||||
|
||||
var msg = TbMsg.newMsg("GET", new DeviceId(EntityId.NULL_UUID), TbMsgMetaData.EMPTY, "{}");
|
||||
var successMsg = TbMsg.newMsg(
|
||||
"SUCCESS", msg.getOriginator(),
|
||||
msg.getMetaData(), msg.getData()
|
||||
);
|
||||
|
||||
var ctx = mock(TbContext.class);
|
||||
when(ctx.transformMsg(
|
||||
eq(msg), eq(msg.getType()),
|
||||
eq(msg.getOriginator()),
|
||||
eq(msg.getMetaData()),
|
||||
eq(msg.getData())
|
||||
)).thenReturn(successMsg);
|
||||
|
||||
var capturedData = ArgumentCaptor.forClass(String.class);
|
||||
|
||||
when(ctx.transformMsg(
|
||||
eq(msg), eq(msg.getType()),
|
||||
eq(msg.getOriginator()),
|
||||
any(),
|
||||
capturedData.capture()
|
||||
)).thenReturn(successMsg);
|
||||
|
||||
httpClient.processMessage(ctx, msg);
|
||||
|
||||
Awaitility.await()
|
||||
.atMost(30, TimeUnit.SECONDS)
|
||||
.until(() -> {
|
||||
try {
|
||||
verify(ctx, times(1)).tellSuccess(any());
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
verify(ctx, times(1)).tellSuccess(any());
|
||||
verify(ctx, times(0)).tellFailure(any(), any());
|
||||
Assert.assertEquals(successResponseBody, capturedData.getValue());
|
||||
}
|
||||
|
||||
private ClientAndServer setUpDummyServer(String host, String path, String paramKey, String paramVal, String successResponseBody) {
|
||||
var server = startClientAndServer(host, 1080);
|
||||
createGetMethodExpectations(server, path, paramKey, paramVal, successResponseBody);
|
||||
return server;
|
||||
}
|
||||
|
||||
private void createGetMethodExpectations(ClientAndServer server, String path, String paramKey, String paramVal, String successResponseBody) {
|
||||
server.when(
|
||||
request()
|
||||
.withMethod("GET")
|
||||
.withPath(path)
|
||||
.withQueryStringParameter(paramKey, paramVal)
|
||||
).respond(
|
||||
response()
|
||||
.withStatusCode(200)
|
||||
.withBody(successResponseBody)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user