Browse Source

Merge with 2.5.3

pull/3217/head
Andrii Shvaika 6 years ago
parent
commit
9e331a1768
  1. 31
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  2. 56
      application/src/main/java/org/thingsboard/server/controller/DeviceController.java
  3. 2
      application/src/main/java/org/thingsboard/server/service/security/permission/Operation.java
  4. 57
      application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java
  5. 17
      application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
  6. 374
      application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java
  7. 3
      common/dao-api/src/main/java/org/thingsboard/server/dao/audit/AuditLogService.java
  8. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
  9. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java
  10. 3
      common/dao-api/src/main/java/org/thingsboard/server/dao/relation/RelationService.java
  11. 2
      common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java
  12. 4
      common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java
  13. 2
      dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
  14. 3
      dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogDao.java
  15. 1
      dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java
  16. 1
      dao/src/main/java/org/thingsboard/server/dao/audit/DummyAuditLogServiceImpl.java
  17. 17
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java
  18. 45
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
  19. 15
      dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java
  20. 16
      dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java
  21. 3
      dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java
  22. 10
      dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java
  23. 2
      dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java
  24. 11
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java
  25. 13
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java
  26. 4
      ui-ngx/package-lock.json
  27. 8
      ui-ngx/src/app/shared/models/audit-log.models.ts
  28. 4
      ui-ngx/src/assets/locale/locale.constant-en_US.json

31
application/src/main/java/org/thingsboard/server/controller/BaseController.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
@ -26,26 +27,27 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.thingsboard.server.common.data.*;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceInfo;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.EntityViewInfo;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.asset.AssetInfo;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
@ -73,7 +75,6 @@ import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.audit.AuditLogService;
@ -640,6 +641,12 @@ public abstract class BaseController {
case ALARM_CLEAR:
msgType = DataConstants.ALARM_CLEAR;
break;
case ASSIGNED_FROM_TENANT:
msgType = DataConstants.ENTITY_ASSIGNED_FROM_TENANT;
break;
case ASSIGNED_TO_TENANT:
msgType = DataConstants.ENTITY_ASSIGNED_TO_TENANT;
break;
}
if (!StringUtils.isEmpty(msgType)) {
try {
@ -659,6 +666,16 @@ public abstract class BaseController {
String strCustomerName = extractParameter(String.class, 2, additionalInfo);
metaData.putValue("unassignedCustomerId", strCustomerId);
metaData.putValue("unassignedCustomerName", strCustomerName);
} else if (actionType == ActionType.ASSIGNED_FROM_TENANT) {
String strTenantId = extractParameter(String.class, 0, additionalInfo);
String strTenantName = extractParameter(String.class, 1, additionalInfo);
metaData.putValue("assignedFromTenantId", strTenantId);
metaData.putValue("assignedFromTenantName", strTenantName);
} else if (actionType == ActionType.ASSIGNED_TO_TENANT) {
String strTenantId = extractParameter(String.class, 0, additionalInfo);
String strTenantName = extractParameter(String.class, 1, additionalInfo);
metaData.putValue("assignedToTenantId", strTenantId);
metaData.putValue("assignedToTenantName", strTenantName);
}
ObjectNode entityNode;
if (entity != null) {
@ -722,5 +739,13 @@ public abstract class BaseController {
return result;
}
protected <E extends HasName> String entityToStr(E entity) {
try {
return json.writeValueAsString(json.valueToTree(entity));
} catch (JsonProcessingException e) {
log.warn("[{}] Failed to convert entity to string!", entity, e);
}
return null;
}
}

56
application/src/main/java/org/thingsboard/server/controller/DeviceController.java

@ -40,8 +40,10 @@ import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceInfo;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.device.DeviceSearchQuery;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
@ -49,6 +51,9 @@ 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.security.DeviceCredentials;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.device.claim.ClaimResponse;
import org.thingsboard.server.dao.device.claim.ClaimResult;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
@ -71,6 +76,7 @@ public class DeviceController extends BaseController {
private static final String DEVICE_ID = "deviceId";
private static final String DEVICE_NAME = "deviceName";
private static final String TENANT_ID = "tenantId";
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/device/{deviceId}", method = RequestMethod.GET)
@ -547,4 +553,54 @@ public class DeviceController extends BaseController {
}
return DataConstants.DEFAULT_SECRET_KEY;
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/{tenantId}/device/{deviceId}", method = RequestMethod.POST)
@ResponseBody
public Device assignDeviceToTenant(@PathVariable(TENANT_ID) String strTenantId,
@PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
checkParameter(TENANT_ID, strTenantId);
checkParameter(DEVICE_ID, strDeviceId);
try {
DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
Device device = checkDeviceId(deviceId, Operation.ASSIGN_TO_TENANT);
TenantId newTenantId = new TenantId(toUUID(strTenantId));
Tenant newTenant = tenantService.findTenantById(newTenantId);
if (newTenant == null) {
throw new ThingsboardException("Could not find the specified Tenant!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
}
Device assignedDevice = deviceService.assignDeviceToTenant(newTenantId, device);
logEntityAction(getCurrentUser(), deviceId, assignedDevice,
assignedDevice.getCustomerId(),
ActionType.ASSIGNED_TO_TENANT, null, strTenantId, newTenant.getName());
Tenant currentTenant = tenantService.findTenantById(getTenantId());
pushAssignedFromNotification(currentTenant, newTenantId, assignedDevice);
return assignedDevice;
} catch (Exception e) {
logEntityAction(getCurrentUser(), emptyId(EntityType.DEVICE), null,
null,
ActionType.ASSIGNED_TO_TENANT, e, strTenantId);
throw handleException(e);
}
}
private void pushAssignedFromNotification(Tenant currentTenant, TenantId newTenantId, Device assignedDevice) {
String data = entityToStr(assignedDevice);
if (data != null) {
TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_ASSIGNED_FROM_TENANT, assignedDevice.getId(), getMetaDataForAssignedFrom(currentTenant), TbMsgDataType.JSON, data);
tbClusterService.pushMsgToRuleEngine(newTenantId, assignedDevice.getId(), tbMsg, null);
}
}
private TbMsgMetaData getMetaDataForAssignedFrom(Tenant tenant) {
TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("assignedFromTenantId", tenant.getId().getId().toString());
metaData.putValue("assignedFromTenantName", tenant.getName());
return metaData;
}
}

2
application/src/main/java/org/thingsboard/server/service/security/permission/Operation.java

@ -18,6 +18,6 @@ package org.thingsboard.server.service.security.permission;
public enum Operation {
ALL, CREATE, READ, WRITE, DELETE, ASSIGN_TO_CUSTOMER, UNASSIGN_FROM_CUSTOMER, RPC_CALL,
READ_CREDENTIALS, WRITE_CREDENTIALS, READ_ATTRIBUTES, WRITE_ATTRIBUTES, READ_TELEMETRY, WRITE_TELEMETRY, CLAIM_DEVICES
READ_CREDENTIALS, WRITE_CREDENTIALS, READ_ATTRIBUTES, WRITE_ATTRIBUTES, READ_TELEMETRY, WRITE_TELEMETRY, CLAIM_DEVICES, ASSIGN_TO_TENANT
}

57
application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java

@ -15,74 +15,17 @@
*/
package org.thingsboard.server.controller;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.Jwts;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.TestRule;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootContextLoader;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.mock.http.MockHttpInputMessage;
import org.springframework.mock.http.MockHttpOutputMessage;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.context.WebApplicationContext;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.SortOrder;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.config.ThingsboardSecurityConfiguration;
import org.thingsboard.server.service.mail.TestMailService;
import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRequest;
import org.thingsboard.server.service.security.auth.rest.LoginRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
@ActiveProfiles("test")
@RunWith(SpringRunner.class)

17
application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java

@ -218,6 +218,7 @@ public abstract class AbstractWebTest {
}
private Tenant savedDifferentTenant;
protected void loginDifferentTenant() throws Exception {
loginSysAdmin();
Tenant tenant = new Tenant();
@ -313,6 +314,10 @@ public abstract class AbstractWebTest {
return readResponse(doGet(urlTemplate, urlVariables).andExpect(status().isOk()), responseClass);
}
protected <T> T doGet(String urlTemplate, Class<T> responseClass, ResultMatcher resultMatcher, Object... urlVariables) throws Exception {
return readResponse(doGet(urlTemplate, urlVariables).andExpect(resultMatcher), responseClass);
}
protected <T> T doGetAsync(String urlTemplate, Class<T> responseClass, Object... urlVariables) throws Exception {
return readResponse(doGetAsync(urlTemplate, urlVariables).andExpect(status().isOk()), responseClass);
}
@ -352,9 +357,9 @@ public abstract class AbstractWebTest {
return readResponse(doGet(urlTemplate, vars).andExpect(status().isOk()), responseType);
}
protected <T> T doGetTypedWithTimePageLink(String urlTemplate, TypeReference<T> responseType,
TimePageLink pageLink,
Object... urlVariables) throws Exception {
protected <T> T doGetTypedWithTimePageLink(String urlTemplate, TypeReference<T> responseType,
TimePageLink pageLink,
Object... urlVariables) throws Exception {
List<Object> pageLinkVariables = new ArrayList<>();
urlTemplate += "pageSize={pageSize}&page={page}";
pageLinkVariables.add(pageLink.getPageSize());
@ -395,11 +400,11 @@ public abstract class AbstractWebTest {
return readResponse(doPost(urlTemplate, content, params).andExpect(status().isOk()), responseClass);
}
protected <T,R> R doPostWithResponse(String urlTemplate, T content, Class<R> responseClass, String... params) throws Exception {
protected <T, R> R doPostWithResponse(String urlTemplate, T content, Class<R> responseClass, String... params) throws Exception {
return readResponse(doPost(urlTemplate, content, params).andExpect(status().isOk()), responseClass);
}
protected <T,R> R doPostWithTypedResponse(String urlTemplate, T content, TypeReference<R> responseType, String... params) throws Exception {
protected <T, R> R doPostWithTypedResponse(String urlTemplate, T content, TypeReference<R> responseType, String... params) throws Exception {
return readResponse(doPost(urlTemplate, content, params).andExpect(status().isOk()), responseType);
}
@ -430,7 +435,7 @@ public abstract class AbstractWebTest {
return mockMvc.perform(postRequest);
}
protected <T> ResultActions doPostAsync(String urlTemplate, T content, Long timeout, String... params) throws Exception {
protected <T> ResultActions doPostAsync(String urlTemplate, T content, Long timeout, String... params) throws Exception {
MockHttpServletRequestBuilder postRequest = post(urlTemplate, params);
setJwtToken(postRequest);
String json = json(content);

374
application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java

@ -15,74 +15,79 @@
*/
package org.thingsboard.server.controller;
import static org.hamcrest.Matchers.containsString;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.core.type.TypeReference;
import org.apache.commons.lang3.RandomStringUtils;
import org.thingsboard.server.common.data.*;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceCredentialsId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.dao.model.ModelConstants;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.fasterxml.jackson.core.type.TypeReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
private IdComparator<Device> idComparator = new IdComparator<>();
private Tenant savedTenant;
private User tenantAdmin;
@Before
public void beforeTest() throws Exception {
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
tenantAdmin = new User();
tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
tenantAdmin.setTenantId(savedTenant.getId());
tenantAdmin.setEmail("tenant2@thingsboard.org");
tenantAdmin.setFirstName("Joe");
tenantAdmin.setLastName("Downs");
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
}
@After
public void afterTest() throws Exception {
loginSysAdmin();
doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
.andExpect(status().isOk());
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk());
}
@Test
public void testSaveDevice() throws Exception {
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
Assert.assertNotNull(savedDevice);
Assert.assertNotNull(savedDevice.getId());
Assert.assertTrue(savedDevice.getCreatedTime() > 0);
@ -90,9 +95,9 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
Assert.assertNotNull(savedDevice.getCustomerId());
Assert.assertEquals(NULL_UUID, savedDevice.getCustomerId().getId());
Assert.assertEquals(device.getName(), savedDevice.getName());
DeviceCredentials deviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
DeviceCredentials deviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
Assert.assertNotNull(deviceCredentials);
Assert.assertNotNull(deviceCredentials.getId());
@ -100,10 +105,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
Assert.assertEquals(DeviceCredentialsType.ACCESS_TOKEN, deviceCredentials.getCredentialsType());
Assert.assertNotNull(deviceCredentials.getCredentialsId());
Assert.assertEquals(20, deviceCredentials.getCredentialsId().length());
savedDevice.setName("My new device");
doPost("/api/device", savedDevice, Device.class);
Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class);
Assert.assertEquals(foundDevice.getName(), savedDevice.getName());
}
@ -115,10 +120,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
loginDifferentTenant();
doPost("/api/device", savedDevice, Device.class, status().isForbidden());
doPost("/api/device", savedDevice, Device.class, status().isNotFound());
deleteDifferentTenant();
}
@Test
public void testFindDeviceById() throws Exception {
Device device = new Device();
@ -133,26 +138,27 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
@Test
public void testFindDeviceTypesByTenantId() throws Exception {
List<Device> devices = new ArrayList<>();
for (int i=0;i<3;i++) {
for (int i = 0; i < 3; i++) {
Device device = new Device();
device.setName("My device B"+i);
device.setName("My device B" + i);
device.setType("typeB");
devices.add(doPost("/api/device", device, Device.class));
}
for (int i=0;i<7;i++) {
for (int i = 0; i < 7; i++) {
Device device = new Device();
device.setName("My device C"+i);
device.setName("My device C" + i);
device.setType("typeC");
devices.add(doPost("/api/device", device, Device.class));
}
for (int i=0;i<9;i++) {
for (int i = 0; i < 9; i++) {
Device device = new Device();
device.setName("My device A"+i);
device.setName("My device A" + i);
device.setType("typeA");
devices.add(doPost("/api/device", device, Device.class));
}
List<EntitySubtype> deviceTypes = doGetTyped("/api/device/types",
new TypeReference<List<EntitySubtype>>(){});
new TypeReference<List<EntitySubtype>>() {
});
Assert.assertNotNull(deviceTypes);
Assert.assertEquals(3, deviceTypes.size());
@ -160,19 +166,19 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
Assert.assertEquals("typeB", deviceTypes.get(1).getType());
Assert.assertEquals("typeC", deviceTypes.get(2).getType());
}
@Test
public void testDeleteDevice() throws Exception {
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
doDelete("/api/device/"+savedDevice.getId().getId().toString())
.andExpect(status().isOk());
doGet("/api/device/"+savedDevice.getId().getId().toString())
.andExpect(status().isNotFound());
doDelete("/api/device/" + savedDevice.getId().getId().toString())
.andExpect(status().isOk());
doGet("/api/device/" + savedDevice.getId().getId().toString())
.andExpect(status().isNotFound());
}
@Test
@ -189,52 +195,51 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
Device device = new Device();
device.setType("default");
doPost("/api/device", device)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Device name should be specified")));
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Device name should be specified")));
}
@Test
public void testAssignUnassignDeviceToCustomer() throws Exception {
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
Customer customer = new Customer();
customer.setTitle("My customer");
Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
Device assignedDevice = doPost("/api/customer/" + savedCustomer.getId().getId().toString()
Device assignedDevice = doPost("/api/customer/" + savedCustomer.getId().getId().toString()
+ "/device/" + savedDevice.getId().getId().toString(), Device.class);
Assert.assertEquals(savedCustomer.getId(), assignedDevice.getCustomerId());
Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class);
Assert.assertEquals(savedCustomer.getId(), foundDevice.getCustomerId());
Device unassignedDevice =
Device unassignedDevice =
doDelete("/api/customer/device/" + savedDevice.getId().getId().toString(), Device.class);
Assert.assertEquals(ModelConstants.NULL_UUID, unassignedDevice.getCustomerId().getId());
foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class);
Assert.assertEquals(ModelConstants.NULL_UUID, foundDevice.getCustomerId().getId());
}
@Test
public void testAssignDeviceToNonExistentCustomer() throws Exception {
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
doPost("/api/customer/" + Uuids.timeBased().toString()
+ "/device/" + savedDevice.getId().getId().toString())
.andExpect(status().isNotFound());
.andExpect(status().isNotFound());
}
@Test
public void testAssignDeviceToCustomerFromDifferentTenant() throws Exception {
loginSysAdmin();
Tenant tenant2 = new Tenant();
tenant2.setTitle("Different tenant");
Tenant savedTenant2 = doPost("/api/tenant", tenant2, Tenant.class);
@ -246,103 +251,103 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
tenantAdmin2.setEmail("tenant3@thingsboard.org");
tenantAdmin2.setFirstName("Joe");
tenantAdmin2.setLastName("Downs");
tenantAdmin2 = createUserAndLogin(tenantAdmin2, "testPassword1");
Customer customer = new Customer();
customer.setTitle("Different customer");
Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
login(tenantAdmin.getEmail(), "testPassword1");
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
doPost("/api/customer/" + savedCustomer.getId().getId().toString()
+ "/device/" + savedDevice.getId().getId().toString())
.andExpect(status().isForbidden());
.andExpect(status().isForbidden());
loginSysAdmin();
doDelete("/api/tenant/"+savedTenant2.getId().getId().toString())
.andExpect(status().isOk());
doDelete("/api/tenant/" + savedTenant2.getId().getId().toString())
.andExpect(status().isOk());
}
@Test
public void testFindDeviceCredentialsByDeviceId() throws Exception {
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
DeviceCredentials deviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());
}
@Test
public void testSaveDeviceCredentials() throws Exception {
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
DeviceCredentials deviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/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());
DeviceCredentials foundDeviceCredentials =
.andExpect(status().isOk());
DeviceCredentials foundDeviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
Assert.assertEquals(deviceCredentials, foundDeviceCredentials);
}
@Test
public void testSaveDeviceCredentialsWithEmptyDevice() throws Exception {
DeviceCredentials deviceCredentials = new DeviceCredentials();
doPost("/api/device/credentials", deviceCredentials)
.andExpect(status().isBadRequest());
.andExpect(status().isBadRequest());
}
@Test
public void testSaveDeviceCredentialsWithEmptyCredentialsType() throws Exception {
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
DeviceCredentials deviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
deviceCredentials.setCredentialsType(null);
doPost("/api/device/credentials", deviceCredentials)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Device credentials type should be specified")));
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Device credentials type should be specified")));
}
@Test
public void testSaveDeviceCredentialsWithEmptyCredentialsId() throws Exception {
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
DeviceCredentials deviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
deviceCredentials.setCredentialsId(null);
doPost("/api/device/credentials", deviceCredentials)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Device credentials id should be specified")));
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Device credentials id should be specified")));
}
@Test
public void testSaveNonExistentDeviceCredentials() throws Exception {
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
DeviceCredentials deviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
DeviceCredentials newDeviceCredentials = new DeviceCredentials(new DeviceCredentialsId(Uuids.timeBased()));
newDeviceCredentials.setCreatedTime(deviceCredentials.getCreatedTime());
@ -350,29 +355,29 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
newDeviceCredentials.setCredentialsType(deviceCredentials.getCredentialsType());
newDeviceCredentials.setCredentialsId(deviceCredentials.getCredentialsId());
doPost("/api/device/credentials", newDeviceCredentials)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Unable to update non-existent device credentials")));
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Unable to update non-existent device credentials")));
}
@Test
public void testSaveDeviceCredentialsWithNonExistentDevice() throws Exception {
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
DeviceCredentials deviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
deviceCredentials.setDeviceId(new DeviceId(Uuids.timeBased()));
doPost("/api/device/credentials", deviceCredentials)
.andExpect(status().isNotFound());
.andExpect(status().isNotFound());
}
@Test
public void testFindTenantDevices() throws Exception {
List<Device> devices = new ArrayList<>();
for (int i=0;i<178;i++) {
for (int i = 0; i < 178; i++) {
Device device = new Device();
device.setName("Device"+i);
device.setName("Device" + i);
device.setType("default");
devices.add(doPost("/api/device", device, Device.class));
}
@ -380,28 +385,29 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
PageLink pageLink = new PageLink(23);
PageData<Device> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
loadedDevices.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
Collections.sort(devices, idComparator);
Collections.sort(loadedDevices, idComparator);
Assert.assertEquals(devices, loadedDevices);
}
@Test
public void testFindTenantDevicesByName() throws Exception {
String title1 = "Device title 1";
List<Device> devicesTitle1 = new ArrayList<>();
for (int i=0;i<143;i++) {
for (int i = 0; i < 143; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title1+suffix;
String name = title1 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType("default");
@ -409,37 +415,37 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
}
String title2 = "Device title 2";
List<Device> devicesTitle2 = new ArrayList<>();
for (int i=0;i<75;i++) {
for (int i = 0; i < 75; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title2+suffix;
String name = title2 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType("default");
devicesTitle2.add(doPost("/api/device", device, Device.class));
}
List<Device> loadedDevicesTitle1 = new ArrayList<>();
PageLink pageLink = new PageLink(15, 0, title1);
PageData<Device> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
loadedDevicesTitle1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
Collections.sort(devicesTitle1, idComparator);
Collections.sort(loadedDevicesTitle1, idComparator);
Assert.assertEquals(devicesTitle1, loadedDevicesTitle1);
List<Device> loadedDevicesTitle2 = new ArrayList<>();
pageLink = new PageLink(4, 0, title2);
do {
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
loadedDevicesTitle2.addAll(pageData.getData());
if (pageData.hasNext()) {
@ -449,25 +455,23 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
Collections.sort(devicesTitle2, idComparator);
Collections.sort(loadedDevicesTitle2, idComparator);
Assert.assertEquals(devicesTitle2, loadedDevicesTitle2);
for (Device device : loadedDevicesTitle1) {
doDelete("/api/device/"+device.getId().getId().toString())
.andExpect(status().isOk());
doDelete("/api/device/" + device.getId().getId().toString())
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, title1);
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
for (Device device : loadedDevicesTitle2) {
doDelete("/api/device/"+device.getId().getId().toString())
.andExpect(status().isOk());
doDelete("/api/device/" + device.getId().getId().toString())
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, title2);
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
@ -480,10 +484,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
String title1 = "Device title 1";
String type1 = "typeA";
List<Device> devicesType1 = new ArrayList<>();
for (int i=0;i<143;i++) {
for (int i = 0; i < 143; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title1+suffix;
String name = title1 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType(type1);
@ -492,10 +496,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
String title2 = "Device title 2";
String type2 = "typeB";
List<Device> devicesType2 = new ArrayList<>();
for (int i=0;i<75;i++) {
for (int i = 0; i < 75; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title2+suffix;
String name = title2 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType(type2);
@ -536,7 +540,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
Assert.assertEquals(devicesType2, loadedDevicesType2);
for (Device device : loadedDevicesType1) {
doDelete("/api/device/"+device.getId().getId().toString())
doDelete("/api/device/" + device.getId().getId().toString())
.andExpect(status().isOk());
}
@ -547,7 +551,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
Assert.assertEquals(0, pageData.getData().size());
for (Device device : loadedDevicesType2) {
doDelete("/api/device/"+device.getId().getId().toString())
doDelete("/api/device/" + device.getId().getId().toString())
.andExpect(status().isOk());
}
@ -557,42 +561,42 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
@Test
public void testFindCustomerDevices() throws Exception {
Customer customer = new Customer();
customer.setTitle("Test customer");
customer = doPost("/api/customer", customer, Customer.class);
CustomerId customerId = customer.getId();
List<Device> devices = new ArrayList<>();
for (int i=0;i<128;i++) {
for (int i = 0; i < 128; i++) {
Device device = new Device();
device.setName("Device"+i);
device.setName("Device" + i);
device.setType("default");
device = doPost("/api/device", device, Device.class);
devices.add(doPost("/api/customer/" + customerId.getId().toString()
+ "/device/" + device.getId().getId().toString(), Device.class));
devices.add(doPost("/api/customer/" + customerId.getId().toString()
+ "/device/" + device.getId().getId().toString(), Device.class));
}
List<Device> loadedDevices = new ArrayList<>();
PageLink pageLink = new PageLink(23);
PageData<Device> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
loadedDevices.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
Collections.sort(devices, idComparator);
Collections.sort(loadedDevices, idComparator);
Assert.assertEquals(devices, loadedDevices);
}
@Test
public void testFindCustomerDevicesByName() throws Exception {
Customer customer = new Customer();
@ -602,52 +606,52 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
String title1 = "Device title 1";
List<Device> devicesTitle1 = new ArrayList<>();
for (int i=0;i<125;i++) {
for (int i = 0; i < 125; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title1+suffix;
String name = title1 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType("default");
device = doPost("/api/device", device, Device.class);
devicesTitle1.add(doPost("/api/customer/" + customerId.getId().toString()
devicesTitle1.add(doPost("/api/customer/" + customerId.getId().toString()
+ "/device/" + device.getId().getId().toString(), Device.class));
}
String title2 = "Device title 2";
List<Device> devicesTitle2 = new ArrayList<>();
for (int i=0;i<143;i++) {
for (int i = 0; i < 143; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title2+suffix;
String name = title2 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType("default");
device = doPost("/api/device", device, Device.class);
devicesTitle2.add(doPost("/api/customer/" + customerId.getId().toString()
devicesTitle2.add(doPost("/api/customer/" + customerId.getId().toString()
+ "/device/" + device.getId().getId().toString(), Device.class));
}
List<Device> loadedDevicesTitle1 = new ArrayList<>();
PageLink pageLink = new PageLink(15, 0, title1);
PageData<Device> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
loadedDevicesTitle1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
Collections.sort(devicesTitle1, idComparator);
Collections.sort(loadedDevicesTitle1, idComparator);
Assert.assertEquals(devicesTitle1, loadedDevicesTitle1);
List<Device> loadedDevicesTitle2 = new ArrayList<>();
pageLink = new PageLink(4, 0, title2);
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
loadedDevicesTitle2.addAll(pageData.getData());
if (pageData.hasNext()) {
@ -657,25 +661,23 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
Collections.sort(devicesTitle2, idComparator);
Collections.sort(loadedDevicesTitle2, idComparator);
Assert.assertEquals(devicesTitle2, loadedDevicesTitle2);
for (Device device : loadedDevicesTitle1) {
doDelete("/api/customer/device/" + device.getId().getId().toString())
.andExpect(status().isOk());
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, title1);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
for (Device device : loadedDevicesTitle2) {
doDelete("/api/customer/device/" + device.getId().getId().toString())
.andExpect(status().isOk());
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, title2);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
@ -693,10 +695,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
String title1 = "Device title 1";
String type1 = "typeC";
List<Device> devicesType1 = new ArrayList<>();
for (int i=0;i<125;i++) {
for (int i = 0; i < 125; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title1+suffix;
String name = title1 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType(type1);
@ -707,10 +709,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
String title2 = "Device title 2";
String type2 = "typeD";
List<Device> devicesType2 = new ArrayList<>();
for (int i=0;i<143;i++) {
for (int i = 0; i < 143; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title2+suffix;
String name = title2 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType(type2);
@ -775,4 +777,54 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
Assert.assertEquals(0, pageData.getData().size());
}
@Test
public void testAssignDeviceToTenant() throws Exception {
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
Device anotherDevice = new Device();
anotherDevice.setName("My device1");
anotherDevice.setType("default");
Device savedAnotherDevice = doPost("/api/device", anotherDevice, Device.class);
EntityRelation relation = new EntityRelation();
relation.setFrom(savedDevice.getId());
relation.setTo(savedAnotherDevice.getId());
relation.setTypeGroup(RelationTypeGroup.COMMON);
relation.setType("Contains");
doPost("/api/relation", relation).andExpect(status().isOk());
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("Different tenant");
Tenant savedDifferentTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedDifferentTenant);
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
user.setTenantId(savedDifferentTenant.getId());
user.setEmail("tenant9@thingsboard.org");
user.setFirstName("Sam");
user.setLastName("Downs");
createUserAndLogin(user, "testPassword1");
login("tenant2@thingsboard.org", "testPassword1");
Device assignedDevice = doPost("/api/tenant/" + savedDifferentTenant.getId().getId() + "/device/" + savedDevice.getId().getId(), Device.class);
doGet("/api/device/" + assignedDevice.getId().getId().toString(), Device.class, status().isNotFound());
login("tenant9@thingsboard.org", "testPassword1");
Device foundDevice1 = doGet("/api/device/" + assignedDevice.getId().getId().toString(), Device.class);
Assert.assertNotNull(foundDevice1);
doGet("/api/relation?fromId=" + savedDevice.getId().getId() + "&fromType=DEVICE&relationType=Contains&toId=" + savedAnotherDevice.getId().getId() + "&toType=DEVICE", EntityRelation.class, status().isNotFound());
loginSysAdmin();
doDelete("/api/tenant/" + savedDifferentTenant.getId().getId().toString())
.andExpect(status().isOk());
}
}

3
common/dao-api/src/main/java/org/thingsboard/server/dao/audit/AuditLogService.java

@ -16,14 +16,12 @@
package org.thingsboard.server.dao.audit;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.audit.AuditLog;
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.data.id.UUIDBased;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.TimePageLink;
@ -49,5 +47,4 @@ public interface AuditLogService {
E entity,
ActionType actionType,
Exception e, Object... additionalInfo);
}

2
common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java

@ -76,4 +76,6 @@ public interface DeviceService {
ListenableFuture<List<EntitySubtype>> findDeviceTypesByTenantId(TenantId tenantId);
Device assignDeviceToTenant(TenantId tenantId, Device device);
}

2
common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java

@ -41,4 +41,6 @@ public interface EventService {
List<Event> findLatestEvents(TenantId tenantId, EntityId entityId, String eventType, int limit);
void removeEvents(TenantId tenantId, EntityId entityId);
}

3
common/dao-api/src/main/java/org/thingsboard/server/dao/relation/RelationService.java

@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import java.util.List;
import java.util.concurrent.ExecutionException;
/**
* Created by ashvayka on 27.04.17.
@ -77,6 +76,8 @@ public interface RelationService {
ListenableFuture<List<EntityRelationInfo>> findInfoByQuery(TenantId tenantId, EntityRelationsQuery query);
void removeRelations(TenantId tenantId, EntityId entityId);
// TODO: This method may be useful for some validations in the future
// ListenableFuture<Boolean> checkRecursiveRelation(EntityId from, EntityId to);

2
common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java

@ -57,6 +57,8 @@ public class DataConstants {
public static final String ATTRIBUTES_DELETED = "ATTRIBUTES_DELETED";
public static final String ALARM_ACK = "ALARM_ACK";
public static final String ALARM_CLEAR = "ALARM_CLEAR";
public static final String ENTITY_ASSIGNED_FROM_TENANT = "ENTITY_ASSIGNED_FROM_TENANT";
public static final String ENTITY_ASSIGNED_TO_TENANT = "ENTITY_ASSIGNED_TO_TENANT";
public static final String RPC_CALL_FROM_SERVER_TO_DEVICE = "RPC_CALL_FROM_SERVER_TO_DEVICE";

4
common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java

@ -40,7 +40,9 @@ public enum ActionType {
ALARM_CLEAR(false),
LOGIN(false),
LOGOUT(false),
LOCKOUT(false);
LOCKOUT(false),
ASSIGNED_FROM_TENANT(false),
ASSIGNED_TO_TENANT(false);
private final boolean isRead;

2
dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java

@ -163,7 +163,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
try {
List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(asset.getTenantId(), assetId).get();
if (entityViews != null && !entityViews.isEmpty()) {
throw new DataValidationException("Can't delete asset that is assigned to entity views!");
throw new DataValidationException("Can't delete asset that has entity views!");
}
} catch (ExecutionException | InterruptedException e) {
log.error("Exception while finding entity views for assetId [{}]", assetId, e);

3
dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogDao.java

@ -23,11 +23,12 @@ import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.Dao;
import java.util.List;
import java.util.UUID;
public interface AuditLogDao {
public interface AuditLogDao extends Dao<AuditLog> {
ListenableFuture<Void> saveByTenantId(AuditLog auditLog);

1
dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java

@ -163,6 +163,7 @@ public class AuditLogServiceImpl implements AuditLogService {
case ALARM_ACK:
case ALARM_CLEAR:
case RELATIONS_DELETED:
case ASSIGNED_TO_TENANT:
if (entity != null) {
ObjectNode entityNode = objectMapper.valueToTree(entity);
if (entityId.getEntityType() == EntityType.DASHBOARD) {

1
dao/src/main/java/org/thingsboard/server/dao/audit/DummyAuditLogServiceImpl.java

@ -58,5 +58,4 @@ public class DummyAuditLogServiceImpl implements AuditLogService {
public <E extends HasName, I extends EntityId> ListenableFuture<List<Void>> logEntityAction(TenantId tenantId, CustomerId customerId, UserId userId, String userName, I entityId, E entity, ActionType actionType, Exception e, Object... additionalInfo) {
return null;
}
}

17
dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java

@ -166,4 +166,21 @@ public interface DeviceDao extends Dao<Device> {
* @return the list of tenant device type objects
*/
ListenableFuture<List<EntitySubtype>> findTenantDeviceTypesAsync(UUID tenantId);
/**
* Find devices by tenantId and device id.
* @param tenantId the tenant Id
* @param id the device Id
* @return the device object
*/
Device findDeviceByTenantIdAndId(TenantId tenantId, UUID id);
/**
* Find devices by tenantId and device id.
* @param tenantId tenantId the tenantId
* @param id the deviceId
* @return the device object
*/
ListenableFuture<Device> findDeviceByTenantIdAndIdAsync(TenantId tenantId, UUID id);
}

45
dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java

@ -28,6 +28,8 @@ import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
@ -50,6 +52,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.dao.customer.CustomerDao;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
@ -98,6 +101,9 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
@Autowired
private CacheManager cacheManager;
@Autowired
private EventService eventService;
@Override
public DeviceInfo findDeviceInfoById(TenantId tenantId, DeviceId deviceId) {
log.trace("Executing findDeviceInfoById [{}]", deviceId);
@ -109,14 +115,22 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
public Device findDeviceById(TenantId tenantId, DeviceId deviceId) {
log.trace("Executing findDeviceById [{}]", deviceId);
validateId(deviceId, INCORRECT_DEVICE_ID + deviceId);
return deviceDao.findById(tenantId, deviceId.getId());
if (TenantId.SYS_TENANT_ID.equals(tenantId)) {
return deviceDao.findById(tenantId, deviceId.getId());
} else {
return deviceDao.findDeviceByTenantIdAndId(tenantId, deviceId.getId());
}
}
@Override
public ListenableFuture<Device> findDeviceByIdAsync(TenantId tenantId, DeviceId deviceId) {
log.trace("Executing findDeviceById [{}]", deviceId);
validateId(deviceId, INCORRECT_DEVICE_ID + deviceId);
return deviceDao.findByIdAsync(tenantId, deviceId.getId());
if (TenantId.SYS_TENANT_ID.equals(tenantId)) {
return deviceDao.findByIdAsync(tenantId, deviceId.getId());
} else {
return deviceDao.findDeviceByTenantIdAndIdAsync(tenantId, deviceId.getId());
}
}
@Cacheable(cacheNames = DEVICE_CACHE, key = "{#tenantId, #name}")
@ -187,7 +201,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
try {
List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(device.getTenantId(), deviceId).get();
if (entityViews != null && !entityViews.isEmpty()) {
throw new DataValidationException("Can't delete device that is assigned to entity views!");
throw new DataValidationException("Can't delete device that has entity views!");
}
} catch (ExecutionException | InterruptedException e) {
log.error("Exception while finding entity views for deviceId [{}]", deviceId, e);
@ -353,6 +367,31 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
}, MoreExecutors.directExecutor());
}
@Transactional
@CacheEvict(cacheNames = DEVICE_CACHE, key = "{#device.tenantId, #device.name}")
@Override
public Device assignDeviceToTenant(TenantId tenantId, Device device) {
log.trace("Executing assignDeviceToTenant [{}][{}]", tenantId, device);
try {
List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(device.getTenantId(), device.getId()).get();
if (!CollectionUtils.isEmpty(entityViews)) {
throw new DataValidationException("Can't assign device that has entity views to another tenant!");
}
} catch (ExecutionException | InterruptedException e) {
log.error("Exception while finding entity views for deviceId [{}]", device.getId(), e);
throw new RuntimeException("Exception while finding entity views for deviceId [" + device.getId() + "]", e);
}
eventService.removeEvents(device.getTenantId(), device.getId());
relationService.removeRelations(device.getTenantId(), device.getId());
device.setTenantId(tenantId);
device.setCustomerId(null);
return doSaveDevice(device, null);
}
private DataValidator<Device> deviceValidator =
new DataValidator<Device>() {

15
dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java

@ -92,6 +92,21 @@ public class BaseEventService implements EventService {
return eventDao.findLatestEvents(tenantId.getId(), entityId, eventType, limit);
}
@Override
public void removeEvents(TenantId tenantId, EntityId entityId) {
PageData<Event> eventPageData;
TimePageLink eventPageLink = new TimePageLink(1000);
do {
eventPageData = findEvents(tenantId, entityId, eventPageLink);
for (Event event : eventPageData.getData()) {
eventDao.removeById(tenantId, event.getUuidId());
}
if (eventPageData.hasNext()) {
eventPageLink = eventPageLink.nextPageLink();
}
} while (eventPageData.hasNext());
}
private DataValidator<Event> eventValidator =
new DataValidator<Event>() {
@Override

16
dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java

@ -506,6 +506,22 @@ public class BaseRelationService implements RelationService {
}, MoreExecutors.directExecutor());
}
@Override
public void removeRelations(TenantId tenantId, EntityId entityId) {
Cache cache = cacheManager.getCache(RELATIONS_CACHE);
List<EntityRelation> relations = new ArrayList<>();
for (RelationTypeGroup relationTypeGroup : RelationTypeGroup.values()) {
relations.addAll(findByFrom(tenantId, entityId, relationTypeGroup));
relations.addAll(findByTo(tenantId, entityId, relationTypeGroup));
}
for (EntityRelation relation : relations) {
cacheEviction(relation, cache);
deleteRelation(tenantId, relation);
}
}
protected void validate(EntityRelation relation) {
if (relation == null) {
throw new DataValidationException("Relation type should be specified!");

3
dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java

@ -127,4 +127,7 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit
List<DeviceEntity> findDevicesByTenantIdAndCustomerIdAndIdIn(UUID tenantId, UUID customerId, List<UUID> deviceIds);
List<DeviceEntity> findDevicesByTenantIdAndIdIn(UUID tenantId, List<UUID> deviceIds);
DeviceEntity findByTenantIdAndId(UUID tenantId, UUID id);
}

10
dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java

@ -175,6 +175,16 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device>
return service.submit(() -> convertTenantDeviceTypesToDto(tenantId, deviceRepository.findTenantDeviceTypes(tenantId)));
}
@Override
public Device findDeviceByTenantIdAndId(TenantId tenantId, UUID id) {
return DaoUtil.getData(deviceRepository.findByTenantIdAndId(tenantId.getId(), id));
}
@Override
public ListenableFuture<Device> findDeviceByTenantIdAndIdAsync(TenantId tenantId, UUID id) {
return service.submit(() -> DaoUtil.getData(deviceRepository.findByTenantIdAndId(tenantId.getId(), id)));
}
private List<EntitySubtype> convertTenantDeviceTypesToDto(UUID tenantId, List<String> types) {
List<EntitySubtype> list = Collections.emptyList();
if (types != null && !types.isEmpty()) {

2
dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java

@ -113,7 +113,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
public User saveUser(User user) {
log.trace("Executing saveUser [{}]", user);
userValidator.validate(user, User::getTenantId);
if (user.getId() == null && !userLoginCaseSensitive) {
if (!userLoginCaseSensitive) {
user.setEmail(user.getEmail().toLowerCase());
}
User savedUser = userDao.save(user.getTenantId(), user);

11
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java

@ -25,8 +25,10 @@ import org.thingsboard.rule.engine.api.TbContext;
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.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg;
@ -56,8 +58,13 @@ public class TbClearAlarmNode extends TbAbstractAlarmNode<TbClearAlarmNodeConfig
@Override
protected ListenableFuture<AlarmResult> processAlarm(TbContext ctx, TbMsg msg) {
String alarmType = TbNodeUtils.processPattern(this.config.getAlarmType(), msg.getMetaData());
ListenableFuture<Alarm> latest = ctx.getAlarmService().findLatestByOriginatorAndType(ctx.getTenantId(), msg.getOriginator(), alarmType);
return Futures.transformAsync(latest, a -> {
ListenableFuture<Alarm> alarmFuture;
if(msg.getOriginator().getEntityType().equals(EntityType.ALARM)){
alarmFuture = ctx.getAlarmService().findAlarmByIdAsync(ctx.getTenantId(), new AlarmId(msg.getOriginator().getId()));
} else {
alarmFuture = ctx.getAlarmService().findLatestByOriginatorAndType(ctx.getTenantId(), msg.getOriginator(), alarmType);
}
return Futures.transformAsync(alarmFuture, a -> {
if (a != null && !a.getStatus().isCleared()) {
return clearAlarm(ctx, msg, a);
}

13
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java

@ -16,8 +16,13 @@
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.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.rule.engine.api.*;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg;
@ -30,7 +35,7 @@ import org.thingsboard.server.common.msg.session.SessionMsgType;
configClazz = EmptyNodeConfiguration.class,
relationTypes = {"Post attributes", "Post telemetry", "RPC Request from Device", "RPC Request to Device", "Activity Event", "Inactivity Event",
"Connect Event", "Disconnect Event", "Entity Created", "Entity Updated", "Entity Deleted", "Entity Assigned",
"Entity Unassigned", "Attributes Updated", "Attributes Deleted", "Alarm Acknowledged", "Alarm Cleared", "Other"},
"Entity Unassigned", "Attributes Updated", "Attributes Deleted", "Alarm Acknowledged", "Alarm Cleared", "Other", "Entity Assigned From Tenant", "Entity Assigned To Tenant"},
nodeDescription = "Route incoming messages by Message Type",
nodeDetails = "Sends messages with message types <b>\"Post attributes\", \"Post telemetry\", \"RPC Request\"</b> etc. via corresponding chain, otherwise <b>Other</b> chain is used.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
@ -81,6 +86,10 @@ public class TbMsgTypeSwitchNode implements TbNode {
relationType = "Alarm Cleared";
} else if (msg.getType().equals(DataConstants.RPC_CALL_FROM_SERVER_TO_DEVICE)) {
relationType = "RPC Request to Device";
} else if (msg.getType().equals(DataConstants.ENTITY_ASSIGNED_FROM_TENANT)) {
relationType = "Entity Assigned From Tenant";
} else if (msg.getType().equals(DataConstants.ENTITY_ASSIGNED_TO_TENANT)) {
relationType = "Entity Assigned To Tenant";
} else {
relationType = "Other";
}

4
ui-ngx/package-lock.json

@ -8997,10 +8997,10 @@
"integrity": "sha512-4O3GWAYJaauMCILm07weko2rHA8a4kjn7+8Lg4s1d7SxwS/3IpkVD/GljbRrIJ1c1W/XGJ3GbuK7RyYZEJChhw=="
},
"ngx-flowchart": {
"version": "git://github.com/thingsboard/ngx-flowchart.git#7a02f4748b5e7821a883c903107af5f20415d026",
"version": "git://github.com/thingsboard/ngx-flowchart.git#a4157b0eef2eb3646ef920447c7b06b39d54f87f",
"from": "git://github.com/thingsboard/ngx-flowchart.git#master",
"requires": {
"tslib": "^1.13.0"
"tslib": "^1.10.0"
},
"dependencies": {
"tslib": {

8
ui-ngx/src/app/shared/models/audit-log.models.ts

@ -49,7 +49,9 @@ export enum ActionType {
ALARM_CLEAR = 'ALARM_CLEAR',
LOGIN = 'LOGIN',
LOGOUT = 'LOGOUT',
LOCKOUT = 'LOCKOUT'
LOCKOUT = 'LOCKOUT',
ASSIGNED_FROM_TENANT = 'ASSIGNED_FROM_TENANT',
ASSIGNED_TO_TENANT = 'ASSIGNED_TO_TENANT'
}
export enum ActionStatus {
@ -79,7 +81,9 @@ export const actionTypeTranslations = new Map<ActionType, string>(
[ActionType.ALARM_CLEAR, 'audit-log.type-alarm-clear'],
[ActionType.LOGIN, 'audit-log.type-login'],
[ActionType.LOGOUT, 'audit-log.type-logout'],
[ActionType.LOCKOUT, 'audit-log.type-lockout']
[ActionType.LOCKOUT, 'audit-log.type-lockout'],
[ActionType.ASSIGNED_FROM_TENANT, 'audit-log.type-assigned-from-tenant'],
[ActionType.ASSIGNED_TO_TENANT, 'audit-log.type-assigned-to-tenant']
]
);

4
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -371,7 +371,9 @@
"action-data": "Action data",
"failure-details": "Failure details",
"search": "Search audit logs",
"clear-search": "Clear search"
"clear-search": "Clear search",
"type-assigned-from-tenant": "Assigned from Tenant",
"type-assigned-to-tenant": "Assigned to Tenant"
},
"confirm-on-exit": {
"message": "You have unsaved changes. Are you sure you want to leave this page?",

Loading…
Cancel
Save