From f54201213c2242514cee7c3ad48096b5027e9a6c Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Fri, 24 Jun 2022 07:20:44 +0300 Subject: [PATCH 01/20] refactoring: tests Dashboard --- .../controller/AbstractNotifyEntityTest.java | 114 ++++++- .../BaseDashboardControllerTest.java | 285 ++++++++++++------ .../controller/BaseDeviceControllerTest.java | 2 + 3 files changed, 309 insertions(+), 92 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java index 2c7f9ae51a..1ab5db4681 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java @@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.dao.audit.AuditLogService; import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.service.gateway_device.GatewayNotificationsService; import java.util.Locale; import java.util.Objects; @@ -49,6 +50,9 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { @SpyBean protected AuditLogService auditLogService; + @SpyBean + protected GatewayNotificationsService gatewayNotificationsService; + protected void testNotifyEntityAllOneTime(HasName entity, EntityId entityId, EntityId originatorId, TenantId tenantId, CustomerId customerId, UserId userId, String userName, ActionType actionType, Object... additionalInfo) { @@ -59,6 +63,16 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.reset(tbClusterService, auditLogService); } + protected void testNotifyEntityAllOneTimeLogEntityActionEntityEqClass(HasName entity, EntityId entityId, EntityId originatorId, + TenantId tenantId, CustomerId customerId, UserId userId, String userName, + ActionType actionType, Object... additionalInfo) { + int cntTime = 1; + testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); + testLogEntityActionEntityEqClass(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); + testPushMsgToRuleEngineTime(originatorId, tenantId, cntTime); + Mockito.reset(tbClusterService, auditLogService); + } + protected void testNotifyEntityNeverMsgToEdgeServiceOneTime(HasName entity, EntityId entityId, TenantId tenantId, ActionType actionType) { testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, 1); @@ -91,6 +105,33 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.reset(tbClusterService, auditLogService); } + protected void testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(HasName entity, HasName originator, + TenantId tenantId, CustomerId customerId, UserId userId, String userName, + ActionType actionType, ActionType actionTypeEdge, int cntTime, int cntTimeEdge, Object... additionalInfo) { + EntityId originatorId = createEntityId_NULL_UUID(originator); + testSendNotificationMsgToEdgeServiceTimeEntityEqAny(tenantId, actionTypeEdge, cntTime); + ArgumentMatcher matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); + ArgumentMatcher matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); + testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, + additionalInfo); + testPushMsgToRuleEngineTime(originatorId, tenantId, cntTimeEdge); + Mockito.reset(tbClusterService, auditLogService); + } + + protected void testNotifyManyEntityManyTimeMsgToEdgeServiceNeverAdditionalInfoAny(HasName entity, HasName originator, + TenantId tenantId, CustomerId customerId, UserId userId, String userName, + ActionType actionType, int cntTime, int cntAdditionalInfo) { + EntityId entityId = createEntityId_NULL_UUID(entity); + EntityId originatorId = createEntityId_NULL_UUID(originator); + testNotificationMsgToEdgeServiceNever(entityId); + ArgumentMatcher matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); + ArgumentMatcher matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); + testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, + cntAdditionalInfo); + testPushMsgToRuleEngineTime(originatorId, tenantId, cntTime); + Mockito.reset(tbClusterService, auditLogService); + } + protected void testNotifyEntityBroadcastEntityStateChangeEventOneTimeMsgToEdgeServiceNever(HasName entity, EntityId entityId, EntityId originatorId, TenantId tenantId, CustomerId customerId, UserId userId, String userName, ActionType actionType, Object... additionalInfo) { @@ -167,6 +208,12 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.eq(edgeTypeByActionType(actionType))); } + private void testSendNotificationMsgToEdgeServiceTimeEntityEqAny(TenantId tenantId, ActionType actionType, int cntTime) { + Mockito.verify(tbClusterService, times(cntTime)).sendNotificationMsgToEdge(Mockito.eq(tenantId), + Mockito.any(), Mockito.any(EntityId.class), Mockito.any(), Mockito.isNull(), + Mockito.eq(edgeTypeByActionType(actionType))); + } + private void testBroadcastEntityStateChangeEventTime(EntityId entityId, TenantId tenantId, int cntTime) { Mockito.verify(tbClusterService, times(cntTime)).broadcastEntityStateChangeEvent(Mockito.eq(tenantId), Mockito.any(entityId.getClass()), Mockito.any(ComponentLifecycleEvent.class)); @@ -181,6 +228,15 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { actionType, cntTime, additionalInfo); } + private void testLogEntityActionEntityEqClass(HasName entity, EntityId originatorId, TenantId tenantId, + CustomerId customerId, UserId userId, String userName, + ActionType actionType, int cntTime, Object... additionalInfo) { + ArgumentMatcher matcherEntityEquals = argument -> argument.getClass().equals(entity.getClass()); + ArgumentMatcher matcherOriginatorId = argument -> argument.equals(originatorId); + testLogEntityActionAdditionalInfo(matcherEntityEquals, matcherOriginatorId, tenantId, customerId, userId, userName, + actionType, cntTime, additionalInfo); + } + private void testLogEntityActionAdditionalInfo(ArgumentMatcher matcherEntity, ArgumentMatcher matcherOriginatorId, TenantId tenantId, CustomerId customerId, UserId userId, String userName, ActionType actionType, int cntTime, Object... additionalInfo) { @@ -237,6 +293,62 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { } } + private void testLogEntityActionAdditionalInfoAny(ArgumentMatcher matcherEntity, ArgumentMatcher matcherOriginatorId, + TenantId tenantId, CustomerId customerId, UserId userId, String userName, + ActionType actionType, int cntTime, int cntAdditionalInfo) { + switch (cntAdditionalInfo) { + case 1: + Mockito.verify(auditLogService, times(cntTime)) + .logEntityAction(Mockito.eq(tenantId), + Mockito.eq(customerId), + Mockito.eq(userId), + Mockito.eq(userName), + Mockito.argThat(matcherOriginatorId), + Mockito.argThat(matcherEntity), + Mockito.eq(actionType), + Mockito.isNull(), + Mockito.anyString()); + break; + case 2: + Mockito.verify(auditLogService, times(cntTime)) + .logEntityAction(Mockito.eq(tenantId), + Mockito.eq(customerId), + Mockito.eq(userId), + Mockito.eq(userName), + Mockito.argThat(matcherOriginatorId), + Mockito.argThat(matcherEntity), + Mockito.eq(actionType), + Mockito.isNull(), + Mockito.anyString(), + Mockito.anyString()); + break; + case 3: + Mockito.verify(auditLogService, times(cntTime)) + .logEntityAction(Mockito.eq(tenantId), + Mockito.eq(customerId), + Mockito.eq(userId), + Mockito.eq(userName), + Mockito.argThat(matcherOriginatorId), + Mockito.argThat(matcherEntity), + Mockito.eq(actionType), + Mockito.isNull(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString()); + break; + default: + Mockito.verify(auditLogService, times(cntTime)) + .logEntityAction(Mockito.eq(tenantId), + Mockito.eq(customerId), + Mockito.eq(userId), + Mockito.eq(userName), + Mockito.argThat(matcherOriginatorId), + Mockito.argThat(matcherEntity), + Mockito.eq(actionType), + Mockito.isNull()); + } + } + private void testLogEntityActionErrorAdditionalInfo(ArgumentMatcher matcherEntity, EntityId originatorId, TenantId tenantId, CustomerId customerId, UserId userId, String userName, ActionType actionType, int cntTime, ArgumentMatcher matcherError, Object... additionalInfo) { @@ -265,7 +377,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.argThat(matcherError), Mockito.eq(extractParameter(String.class, 0, additionalInfo)), Mockito.eq(extractParameter(String.class, 1, additionalInfo))); - case 3: + case 3: Mockito.verify(auditLogService, times(cntTime)) .logEntityAction(Mockito.eq(tenantId), Mockito.eq(customerId), diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java index 170b3ec817..f98d7f324a 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java @@ -22,16 +22,19 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; 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.Tenant; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.dao.exception.DataValidationException; import java.util.ArrayList; import java.util.Collections; @@ -41,54 +44,66 @@ import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; public abstract class BaseDashboardControllerTest extends AbstractControllerTest { - + private IdComparator 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 testSaveDashboard() throws Exception { Dashboard dashboard = new Dashboard(); dashboard.setTitle("My dashboard"); + + Mockito.reset(tbClusterService, auditLogService); + Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class); - + + testNotifyEntityOneTimeMsgToEdgeServiceNever(savedDashboard, savedDashboard.getId(), savedDashboard.getId(), savedTenant.getId(), + tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED); + Assert.assertNotNull(savedDashboard); Assert.assertNotNull(savedDashboard.getId()); Assert.assertTrue(savedDashboard.getCreatedTime() > 0); Assert.assertEquals(savedTenant.getId(), savedDashboard.getTenantId()); Assert.assertEquals(dashboard.getTitle(), savedDashboard.getTitle()); - + savedDashboard.setTitle("My new dashboard"); + + Mockito.reset(tbClusterService, auditLogService); + doPost("/api/dashboard", savedDashboard, Dashboard.class); - + + testNotifyEntityAllOneTime(savedDashboard, savedDashboard.getId(), savedDashboard.getId(), savedTenant.getId(), + tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.UPDATED); + Dashboard foundDashboard = doGet("/api/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); Assert.assertEquals(foundDashboard.getTitle(), savedDashboard.getTitle()); } @@ -97,7 +112,16 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest public void testSaveDashboardInfoWithViolationOfValidation() throws Exception { Dashboard dashboard = new Dashboard(); dashboard.setTitle(RandomStringUtils.randomAlphabetic(300)); - doPost("/api/dashboard", dashboard).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); + String msgError = "length of title must be equal or less than 255"; + + Mockito.reset(tbClusterService, auditLogService); + + doPost("/api/dashboard", dashboard).andExpect(statusReason(containsString(msgError))); + + dashboard.setTenantId(savedTenant.getId()); + testNotifyEntityEqualsOneTimeError(dashboard, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); + Mockito.reset(tbClusterService, auditLogService); } @Test @@ -107,10 +131,16 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class); loginDifferentTenant(); + + Mockito.reset(tbClusterService, auditLogService); + doPost("/api/dashboard", savedDashboard, Dashboard.class, status().isForbidden()); + + testNotifyEntityNever(savedDashboard.getId(), savedDashboard); + deleteDifferentTenant(); } - + @Test public void testFindDashboardById() throws Exception { Dashboard dashboard = new Dashboard(); @@ -120,48 +150,73 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest Assert.assertNotNull(foundDashboard); Assert.assertEquals(savedDashboard, foundDashboard); } - + @Test public void testDeleteDashboard() throws Exception { Dashboard dashboard = new Dashboard(); dashboard.setTitle("My dashboard"); Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class); - - doDelete("/api/dashboard/"+savedDashboard.getId().getId().toString()) - .andExpect(status().isOk()); - doGet("/api/dashboard/"+savedDashboard.getId().getId().toString()) - .andExpect(status().isNotFound()); + Mockito.reset(tbClusterService, auditLogService); + + doDelete("/api/dashboard/" + savedDashboard.getId().getId().toString()).andExpect(status().isOk()); + + testNotifyEntityOneTimeMsgToEdgeServiceNever(savedDashboard, savedDashboard.getId(), savedDashboard.getId(), + savedDashboard.getTenantId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.DELETED, + savedDashboard.getId().getId().toString()); + + + doGet("/api/dashboard/" + savedDashboard.getId().getId().toString()) + .andExpect(status().isNotFound()); } - + @Test public void testSaveDashboardWithEmptyTitle() throws Exception { Dashboard dashboard = new Dashboard(); + String msgError = "Dashboard title should be specified"; + + Mockito.reset(tbClusterService, auditLogService); + doPost("/api/dashboard", dashboard) - .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Dashboard title should be specified"))); + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); + + testNotifyEntityEqualsOneTimeError(dashboard, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); } - + @Test public void testAssignUnassignDashboardToCustomer() throws Exception { Dashboard dashboard = new Dashboard(); dashboard.setTitle("My dashboard"); Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class); - + Customer customer = new Customer(); customer.setTitle("My customer"); Customer savedCustomer = doPost("/api/customer", customer, Customer.class); - - Dashboard assignedDashboard = doPost("/api/customer/" + savedCustomer.getId().getId().toString() + + Mockito.reset(tbClusterService, auditLogService); + + Dashboard assignedDashboard = doPost("/api/customer/" + savedCustomer.getId().getId().toString() + "/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); Assert.assertTrue(assignedDashboard.getAssignedCustomers().contains(savedCustomer.toShortCustomerInfo())); + testNotifyEntityAllOneTimeLogEntityActionEntityEqClass(assignedDashboard, assignedDashboard.getId(), assignedDashboard.getId(), + savedTenant.getId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ASSIGNED_TO_CUSTOMER, + savedDashboard.getId().getId().toString(), savedCustomer.getId().getId().toString(), savedCustomer.getTitle()); + Dashboard foundDashboard = doGet("/api/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); Assert.assertTrue(foundDashboard.getAssignedCustomers().contains(savedCustomer.toShortCustomerInfo())); - Dashboard unassignedDashboard = - doDelete("/api/customer/"+savedCustomer.getId().getId().toString()+"/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); + Mockito.reset(tbClusterService, auditLogService); + + Dashboard unassignedDashboard = + doDelete("/api/customer/" + savedCustomer.getId().getId().toString() + "/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); + + testNotifyEntityAllOneTimeLogEntityActionEntityEqClass(assignedDashboard, assignedDashboard.getId(), assignedDashboard.getId(), + savedTenant.getId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.UNASSIGNED_FROM_CUSTOMER, + savedDashboard.getId().getId().toString(), savedCustomer.getId().getId().toString(), savedCustomer.getTitle()); Assert.assertTrue(unassignedDashboard.getAssignedCustomers() == null || unassignedDashboard.getAssignedCustomers().isEmpty()); @@ -169,22 +224,25 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest Assert.assertTrue(foundDashboard.getAssignedCustomers() == null || foundDashboard.getAssignedCustomers().isEmpty()); } - + @Test public void testAssignDashboardToNonExistentCustomer() throws Exception { Dashboard dashboard = new Dashboard(); dashboard.setTitle("My dashboard"); Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class); - + doPost("/api/customer/" + Uuids.timeBased().toString() + "/dashboard/" + savedDashboard.getId().getId().toString()) - .andExpect(status().isNotFound()); + .andExpect(status().isNotFound()); + + Mockito.reset(tbClusterService, auditLogService); + testNotifyEntityNever(savedDashboard.getId(), savedDashboard); } - + @Test public void testAssignDashboardToCustomerFromDifferentTenant() throws Exception { loginSysAdmin(); - + Tenant tenant2 = new Tenant(); tenant2.setTitle("Different tenant"); Tenant savedTenant2 = doPost("/api/tenant", tenant2, Tenant.class); @@ -196,100 +254,121 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest tenantAdmin2.setEmail("tenant3@thingsboard.org"); tenantAdmin2.setFirstName("Joe"); tenantAdmin2.setLastName("Downs"); - - tenantAdmin2 = createUserAndLogin(tenantAdmin2, "testPassword1"); - + + createUserAndLogin(tenantAdmin2, "testPassword1"); + Customer customer = new Customer(); customer.setTitle("Different customer"); Customer savedCustomer = doPost("/api/customer", customer, Customer.class); login(tenantAdmin.getEmail(), "testPassword1"); - + Dashboard dashboard = new Dashboard(); dashboard.setTitle("My dashboard"); Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class); - + doPost("/api/customer/" + savedCustomer.getId().getId().toString() + "/dashboard/" + savedDashboard.getId().getId().toString()) - .andExpect(status().isForbidden()); - + .andExpect(status().isForbidden()); + + Mockito.reset(tbClusterService, auditLogService); + testNotifyEntityNever(savedDashboard.getId(), savedDashboard); + + doDelete("/api/tenant/" + savedTenant2.getId().getId().toString()) + .andExpect(status().isForbidden()); + testNotifyEntityNever(savedDashboard.getId(), savedDashboard); + loginSysAdmin(); - - doDelete("/api/tenant/"+savedTenant2.getId().getId().toString()) - .andExpect(status().isOk()); + + doDelete("/api/tenant/" + savedTenant2.getId().getId().toString()) + .andExpect(status().isOk()); } @Test public void testFindTenantDashboards() throws Exception { List dashboards = new ArrayList<>(); - for (int i=0;i<173;i++) { + + Mockito.reset(tbClusterService, auditLogService); + + int cntEntity = 173; + for (int i = 0; i < cntEntity; i++) { Dashboard dashboard = new Dashboard(); - dashboard.setTitle("Dashboard"+i); + dashboard.setTitle("Dashboard" + i); dashboards.add(new DashboardInfo(doPost("/api/dashboard", dashboard, Dashboard.class))); } + + testNotifyManyEntityManyTimeMsgToEdgeServiceNever(new Dashboard(), new Dashboard(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ADDED, cntEntity); + List loadedDashboards = new ArrayList<>(); PageLink pageLink = new PageLink(24); PageData pageData = null; do { - pageData = doGetTypedWithPageLink("/api/tenant/dashboards?", - new TypeReference>(){}, pageLink); + pageData = doGetTypedWithPageLink("/api/tenant/dashboards?", + new TypeReference>() { + }, pageLink); loadedDashboards.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); } } while (pageData.hasNext()); - + Collections.sort(dashboards, idComparator); Collections.sort(loadedDashboards, idComparator); - + Assert.assertEquals(dashboards, loadedDashboards); } - + @Test public void testFindTenantDashboardsByTitle() throws Exception { String title1 = "Dashboard title 1"; List dashboardsTitle1 = new ArrayList<>(); - for (int i=0;i<134;i++) { + int cntEntity = 134; + for (int i = 0; i < cntEntity; i++) { Dashboard dashboard = new Dashboard(); - String suffix = RandomStringUtils.randomAlphanumeric((int)(Math.random()*15)); - String title = title1+suffix; + String suffix = RandomStringUtils.randomAlphanumeric((int) (Math.random() * 15)); + String title = title1 + suffix; title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase(); dashboard.setTitle(title); dashboardsTitle1.add(new DashboardInfo(doPost("/api/dashboard", dashboard, Dashboard.class))); } String title2 = "Dashboard title 2"; List dashboardsTitle2 = new ArrayList<>(); - for (int i=0;i<112;i++) { + + for (int i = 0; i < 112; i++) { Dashboard dashboard = new Dashboard(); - String suffix = RandomStringUtils.randomAlphanumeric((int)(Math.random()*15)); - String title = title2+suffix; + String suffix = RandomStringUtils.randomAlphanumeric((int) (Math.random() * 15)); + String title = title2 + suffix; title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase(); dashboard.setTitle(title); dashboardsTitle2.add(new DashboardInfo(doPost("/api/dashboard", dashboard, Dashboard.class))); } - + List loadedDashboardsTitle1 = new ArrayList<>(); PageLink pageLink = new PageLink(15, 0, title1); PageData pageData = null; do { - pageData = doGetTypedWithPageLink("/api/tenant/dashboards?", - new TypeReference>(){}, pageLink); + pageData = doGetTypedWithPageLink("/api/tenant/dashboards?", + new TypeReference>() { + }, pageLink); loadedDashboardsTitle1.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); } } while (pageData.hasNext()); - + Collections.sort(dashboardsTitle1, idComparator); Collections.sort(loadedDashboardsTitle1, idComparator); - + Assert.assertEquals(dashboardsTitle1, loadedDashboardsTitle1); - + List loadedDashboardsTitle2 = new ArrayList<>(); pageLink = new PageLink(4, 0, title2); do { - pageData = doGetTypedWithPageLink("/api/tenant/dashboards?", - new TypeReference>(){}, pageLink); + pageData = doGetTypedWithPageLink("/api/tenant/dashboards?", + new TypeReference>() { + }, pageLink); loadedDashboardsTitle2.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -298,63 +377,79 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest Collections.sort(dashboardsTitle2, idComparator); Collections.sort(loadedDashboardsTitle2, idComparator); - + Assert.assertEquals(dashboardsTitle2, loadedDashboardsTitle2); - + + Mockito.reset(tbClusterService, auditLogService); + for (DashboardInfo dashboard : loadedDashboardsTitle1) { - doDelete("/api/dashboard/"+dashboard.getId().getId().toString()) - .andExpect(status().isOk()); + doDelete("/api/dashboard/" + dashboard.getId().getId().toString()) + .andExpect(status().isOk()); } - + + testNotifyManyEntityManyTimeMsgToEdgeServiceNeverAdditionalInfoAny(new Dashboard(), new Dashboard(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.DELETED, cntEntity, 1); + pageLink = new PageLink(4, 0, title1); - pageData = doGetTypedWithPageLink("/api/tenant/dashboards?", - new TypeReference>(){}, pageLink); + pageData = doGetTypedWithPageLink("/api/tenant/dashboards?", + new TypeReference>() { + }, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); - + for (DashboardInfo dashboard : loadedDashboardsTitle2) { - doDelete("/api/dashboard/"+dashboard.getId().getId().toString()) - .andExpect(status().isOk()); + doDelete("/api/dashboard/" + dashboard.getId().getId().toString()) + .andExpect(status().isOk()); } - + pageLink = new PageLink(4, 0, title2); - pageData = doGetTypedWithPageLink("/api/tenant/dashboards?", - new TypeReference>(){}, pageLink); + pageData = doGetTypedWithPageLink("/api/tenant/dashboards?", + new TypeReference>() { + }, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); } - + @Test public void testFindCustomerDashboards() throws Exception { Customer customer = new Customer(); customer.setTitle("Test customer"); customer = doPost("/api/customer", customer, Customer.class); CustomerId customerId = customer.getId(); - + + Mockito.reset(tbClusterService, auditLogService); + + int cntEntity = 173; List dashboards = new ArrayList<>(); - for (int i=0;i<173;i++) { + for (int i = 0; i < cntEntity; i++) { Dashboard dashboard = new Dashboard(); - dashboard.setTitle("Dashboard"+i); + dashboard.setTitle("Dashboard" + i); dashboard = doPost("/api/dashboard", dashboard, Dashboard.class); dashboards.add(new DashboardInfo(doPost("/api/customer/" + customerId.getId().toString() - + "/dashboard/" + dashboard.getId().getId().toString(), Dashboard.class))); + + "/dashboard/" + dashboard.getId().getId().toString(), Dashboard.class))); } - + + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new Dashboard(), new Dashboard(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ADDED, ActionType.ASSIGNED_TO_CUSTOMER, cntEntity, cntEntity*2); + List loadedDashboards = new ArrayList<>(); PageLink pageLink = new PageLink(21); PageData pageData = null; do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/dashboards?", - new TypeReference>(){}, pageLink); + new TypeReference>() { + }, pageLink); loadedDashboards.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); } } while (pageData.hasNext()); - + Collections.sort(dashboards, idComparator); Collections.sort(loadedDashboards, idComparator); - + Assert.assertEquals(dashboards, loadedDashboards); } @@ -367,11 +462,18 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest dashboard.setTitle("My dashboard"); Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class); + Mockito.reset(tbClusterService, auditLogService); + doPost("/api/edge/" + savedEdge.getId().getId().toString() + "/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); + testNotifyEntityAllOneTime(savedDashboard, savedDashboard.getId(), savedDashboard.getId(), savedTenant.getId(), + tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ASSIGNED_TO_EDGE, + savedDashboard.getId().getId().toString(), savedEdge.getId().getId().toString(), savedEdge.getName()); + PageData pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/dashboards?", - new TypeReference>() {}, new PageLink(100)); + new TypeReference>() { + }, new PageLink(100)); Assert.assertEquals(1, pageData.getData().size()); @@ -379,7 +481,8 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest + "/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/dashboards?", - new TypeReference>() {}, new PageLink(100)); + new TypeReference>() { + }, new PageLink(100)); Assert.assertEquals(0, pageData.getData().size()); } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java index d6e30c644c..8852438b89 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java @@ -214,6 +214,8 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { doDelete("/api/device/" + savedDevice.getId().getId()) .andExpect(status().isOk()); +// gatewayNotificationsService.onDeviceDeleted(device); + doGet("/api/device/" + savedDevice.getId().getId()) .andExpect(status().isNotFound()); } From dcdd3478924651b03d304114fb5ee52f0a346d64 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Mon, 27 Jun 2022 19:57:29 +0300 Subject: [PATCH 02/20] refactoring: tests Device --- .../DefaultTbNotificationEntityService.java | 12 +- .../entitiy/TbNotificationEntityService.java | 4 +- .../device/DefaultTbDeviceService.java | 13 +- .../controller/AbstractNotifyEntityTest.java | 112 ++++++-- .../controller/BaseDeviceControllerTest.java | 261 ++++++++++++++++-- 5 files changed, 338 insertions(+), 64 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java index 76477c974b..f27199d6bb 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java @@ -158,16 +158,16 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS @Override public void notifyUpdateDeviceCredentials(TenantId tenantId, DeviceId deviceId, CustomerId customerId, Device device, - DeviceCredentials deviceCredentials, SecurityUser user) { + DeviceCredentials deviceCredentials, ActionType actionType, SecurityUser user) { tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(tenantId, deviceCredentials.getDeviceId(), deviceCredentials), null); - sendEntityNotificationMsg(tenantId, deviceId, EdgeEventActionType.CREDENTIALS_UPDATED); - logEntityAction(tenantId, deviceId, device, customerId, ActionType.CREDENTIALS_UPDATED, user, deviceCredentials); + sendEntityNotificationMsg(tenantId, deviceId, edgeTypeByActionType(actionType)); + logEntityAction(tenantId, deviceId, device, customerId, actionType, user, deviceCredentials); } @Override public void notifyAssignDeviceToTenant(TenantId tenantId, TenantId newTenantId, DeviceId deviceId, CustomerId customerId, - Device device, Tenant tenant, SecurityUser user, Object... additionalInfo) { - logEntityAction(tenantId, deviceId, device, customerId, ActionType.ASSIGNED_TO_TENANT, user, additionalInfo); + Device device, Tenant tenant, ActionType actionType, SecurityUser user, Object... additionalInfo) { + logEntityAction(tenantId, deviceId, device, customerId, actionType, user, additionalInfo); pushAssignedFromNotification(tenant, newTenantId, device); } @@ -358,6 +358,8 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS return EdgeEventActionType.ASSIGNED_TO_EDGE; case UNASSIGNED_FROM_EDGE: return EdgeEventActionType.UNASSIGNED_FROM_EDGE; + case CREDENTIALS_UPDATED: + return EdgeEventActionType.CREDENTIALS_UPDATED; default: return null; } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java index 5ce91046d6..e7f90527d4 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java @@ -83,10 +83,10 @@ public interface TbNotificationEntityService { List relatedEdgeIds, SecurityUser user, Object... additionalInfo); void notifyUpdateDeviceCredentials(TenantId tenantId, DeviceId deviceId, CustomerId customerId, Device device, - DeviceCredentials deviceCredentials, SecurityUser user); + DeviceCredentials deviceCredentials, ActionType actionType, SecurityUser user); void notifyAssignDeviceToTenant(TenantId tenantId, TenantId newTenantId, DeviceId deviceId, CustomerId customerId, - Device device, Tenant tenant, SecurityUser user, Object... additionalInfo); + Device device, Tenant tenant, ActionType actionType, SecurityUser user, Object... additionalInfo); void notifyEdge(TenantId tenantId, EdgeId edgeId, CustomerId customerId, Edge edge, ActionType actionType, SecurityUser user, Object... additionalInfo); diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java index 12449f60e2..6547e694fc 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java @@ -27,7 +27,6 @@ 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.edge.Edge; -import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; @@ -107,7 +106,7 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T try { Device savedDevice = checkNotNull(deviceService.assignDeviceToCustomer(user.getTenantId(), deviceId, customerId)); notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, deviceId, customerId, savedDevice, - actionType, EdgeEventActionType.ASSIGNED_TO_CUSTOMER, user, true, deviceId.toString(), customerId.toString(), customer.getName()); + actionType, edgeTypeByActionType(actionType), user, true, deviceId.toString(), customerId.toString(), customer.getName()); return savedDevice; } catch (Exception e) { @@ -178,13 +177,14 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T public DeviceCredentials updateDeviceCredentials(Device device, DeviceCredentials deviceCredentials, SecurityUser user) throws ThingsboardException { TenantId tenantId = device.getTenantId(); DeviceId deviceId = device.getId(); + ActionType actionType = ActionType.CREDENTIALS_UPDATED; try { DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(tenantId, deviceCredentials)); - notificationEntityService.notifyUpdateDeviceCredentials(tenantId, deviceId, device.getCustomerId(), device, result, user); + notificationEntityService.notifyUpdateDeviceCredentials(tenantId, deviceId, device.getCustomerId(), device, result, actionType, user); return result; } catch (Exception e) { notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.DEVICE), null, null, - ActionType.CREDENTIALS_UPDATED, user, e, deviceCredentials); + actionType, user, e, deviceCredentials); throw handleException(e); } } @@ -229,17 +229,18 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T public Device assignDeviceToTenant(Device device, Tenant newTenant, SecurityUser user) throws ThingsboardException { TenantId tenantId = device.getTenantId(); TenantId newTenantId = newTenant.getId(); + ActionType actionType = ActionType.ASSIGNED_TO_TENANT; try { Tenant tenant = tenantService.findTenantById(tenantId); Device assignedDevice = deviceService.assignDeviceToTenant(newTenantId, device); notificationEntityService.notifyAssignDeviceToTenant(tenantId, newTenantId, device.getId(), - assignedDevice.getCustomerId(), assignedDevice, tenant, user, newTenantId.toString(), newTenant.getName()); + assignedDevice.getCustomerId(), assignedDevice, tenant, actionType, user, newTenantId.toString(), newTenant.getName()); return assignedDevice; } catch (Exception e) { notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.DEVICE), null, null, - ActionType.ASSIGNED_TO_TENANT, user, e, newTenantId.toString()); + actionType, user, e, newTenantId.toString()); throw handleException(e); } } diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java index 1ab5db4681..7757016cbf 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java @@ -20,6 +20,7 @@ import org.mockito.ArgumentMatcher; import org.mockito.Mockito; import org.springframework.boot.test.mock.mockito.SpyBean; import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.audit.ActionType; @@ -30,10 +31,13 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; import org.thingsboard.server.dao.audit.AuditLogService; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.service.gateway_device.GatewayNotificationsService; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; import java.util.Objects; @@ -100,7 +104,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { ArgumentMatcher matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, - additionalInfo); + extractMatcherAdditionalInfo(additionalInfo)); testPushMsgToRuleEngineTime(originatorId, tenantId, cntTime); Mockito.reset(tbClusterService, auditLogService); } @@ -113,7 +117,20 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { ArgumentMatcher matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, - additionalInfo); + extractMatcherAdditionalInfo(additionalInfo)); + testPushMsgToRuleEngineTime(originatorId, tenantId, cntTimeEdge); + Mockito.reset(tbClusterService, auditLogService); + } + + protected void testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(HasName entity, HasName originator, + TenantId tenantId, CustomerId customerId, UserId userId, String userName, + ActionType actionType, ActionType actionTypeEdge, int cntTime, int cntTimeEdge, int cntAdditionalInfo) { + EntityId originatorId = createEntityId_NULL_UUID(originator); + testSendNotificationMsgToEdgeServiceTimeEntityEqAny(tenantId, actionTypeEdge, cntTime); + ArgumentMatcher matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); + ArgumentMatcher matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); + testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, + cntAdditionalInfo); testPushMsgToRuleEngineTime(originatorId, tenantId, cntTimeEdge); Mockito.reset(tbClusterService, auditLogService); } @@ -143,6 +160,18 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.reset(tbClusterService, auditLogService); } + + protected void testNotifyEntityMsgToEdgePushMsgToCoreOneTime(HasName entity, EntityId entityId, EntityId originatorId, + TenantId tenantId, CustomerId customerId, UserId userId, String userName, + ActionType actionType, Object... additionalInfo) { + int cntTime = 1; + testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); + testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); + tesPushMsgToCoreTime(cntTime); + Mockito.reset(tbClusterService, auditLogService); + } + + protected void testNotifyEntityEqualsOneTimeError(HasName entity, TenantId tenantId, UserId userId, String userName, ActionType actionType, Exception exp, Object... additionalInfo) { @@ -153,7 +182,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { ArgumentMatcher matcherError = argument -> argument.getMessage().contains(exp.getMessage()) & argument.getClass().equals(exp.getClass()); testLogEntityActionErrorAdditionalInfo(matcherEntityEquals, entity_originator_NULL_UUID, tenantId, customer_NULL_UUID, userId, - userName, actionType, 1, matcherError, additionalInfo); + userName, actionType, 1, matcherError, extractMatcherAdditionalInfo(additionalInfo)); testPushMsgToRuleEngineNever(entity_originator_NULL_UUID); } @@ -167,7 +196,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { ArgumentMatcher matcherError = argument -> argument.getMessage().contains(exp.getMessage()) & argument.getClass().equals(exp.getClass()); testLogEntityActionErrorAdditionalInfo(matcherEntityIsNull, entity_originator_NULL_UUID, tenantId, customer_NULL_UUID, - userId, userName, actionType, 1, matcherError, additionalInfo); + userId, userName, actionType, 1, matcherError, extractMatcherAdditionalInfo(additionalInfo)); testPushMsgToRuleEngineNever(entity_originator_NULL_UUID); } @@ -179,6 +208,26 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.reset(tbClusterService, auditLogService); } + protected void testNotificationUpdateGatewayOneTime(Device device, Device oldDevice) { + Mockito.verify(gatewayNotificationsService, times(1)).onDeviceUpdated(Mockito.eq(device), Mockito.eq(oldDevice)); + } + + protected void testNotificationUpdateGatewayTime(int cntTimes) { + Mockito.verify(gatewayNotificationsService, times(cntTimes)).onDeviceUpdated(Mockito.any(Device.class), Mockito.isNull()); + } + + protected void testNotificationUpdateGatewayNever() { + Mockito.verify(gatewayNotificationsService, never()).onDeviceUpdated(Mockito.any(Device.class), Mockito.any(Device.class)); + } + + protected void testNotificationDeleteGatewayOneTime(Device device) { + Mockito.verify(gatewayNotificationsService, times(1)).onDeviceDeleted(device); + } + + protected void testNotificationDeleteGatewayNever() { + Mockito.verify(gatewayNotificationsService, never()).onDeviceDeleted(Mockito.any(Device.class)); + } + private void testNotificationMsgToEdgeServiceNever(EntityId entityId) { Mockito.verify(tbClusterService, never()).sendNotificationMsgToEdge(Mockito.any(), Mockito.any(), Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any(), Mockito.any()); @@ -219,13 +268,17 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.any(entityId.getClass()), Mockito.any(ComponentLifecycleEvent.class)); } + private void tesPushMsgToCoreTime(int cntTime) { + Mockito.verify(tbClusterService, times(cntTime)).pushMsgToCore(Mockito.any(ToDeviceActorNotificationMsg.class), Mockito.isNull()); + } + private void testLogEntityAction(HasName entity, EntityId originatorId, TenantId tenantId, CustomerId customerId, UserId userId, String userName, ActionType actionType, int cntTime, Object... additionalInfo) { ArgumentMatcher matcherEntityEquals = argument -> argument.equals(entity); ArgumentMatcher matcherOriginatorId = argument -> argument.equals(originatorId); testLogEntityActionAdditionalInfo(matcherEntityEquals, matcherOriginatorId, tenantId, customerId, userId, userName, - actionType, cntTime, additionalInfo); + actionType, cntTime, extractMatcherAdditionalInfo(additionalInfo)); } private void testLogEntityActionEntityEqClass(HasName entity, EntityId originatorId, TenantId tenantId, @@ -234,13 +287,13 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { ArgumentMatcher matcherEntityEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher matcherOriginatorId = argument -> argument.equals(originatorId); testLogEntityActionAdditionalInfo(matcherEntityEquals, matcherOriginatorId, tenantId, customerId, userId, userName, - actionType, cntTime, additionalInfo); + actionType, cntTime, extractMatcherAdditionalInfo(additionalInfo)); } private void testLogEntityActionAdditionalInfo(ArgumentMatcher matcherEntity, ArgumentMatcher matcherOriginatorId, TenantId tenantId, CustomerId customerId, UserId userId, String userName, - ActionType actionType, int cntTime, Object... additionalInfo) { - switch (additionalInfo.length) { + ActionType actionType, int cntTime, List> matcherAdditionalInfos) { + switch (matcherAdditionalInfos.size()) { case 1: Mockito.verify(auditLogService, times(cntTime)) .logEntityAction(Mockito.eq(tenantId), @@ -251,7 +304,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.argThat(matcherEntity), Mockito.eq(actionType), Mockito.isNull(), - Mockito.eq(extractParameter(String.class, 0, additionalInfo))); + Mockito.argThat(matcherAdditionalInfos.get(0))); break; case 2: Mockito.verify(auditLogService, times(cntTime)) @@ -263,8 +316,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.argThat(matcherEntity), Mockito.eq(actionType), Mockito.isNull(), - Mockito.eq(extractParameter(String.class, 0, additionalInfo)), - Mockito.eq(extractParameter(String.class, 1, additionalInfo))); + Mockito.argThat(matcherAdditionalInfos.get(0)), + Mockito.argThat(matcherAdditionalInfos.get(1))); break; case 3: Mockito.verify(auditLogService, times(cntTime)) @@ -276,9 +329,9 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.argThat(matcherEntity), Mockito.eq(actionType), Mockito.isNull(), - Mockito.eq(extractParameter(String.class, 0, additionalInfo)), - Mockito.eq(extractParameter(String.class, 1, additionalInfo)), - Mockito.eq(extractParameter(String.class, 2, additionalInfo))); + Mockito.argThat(matcherAdditionalInfos.get(0)), + Mockito.argThat(matcherAdditionalInfos.get(1)), + Mockito.argThat(matcherAdditionalInfos.get(2))); break; default: Mockito.verify(auditLogService, times(cntTime)) @@ -351,8 +404,9 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { private void testLogEntityActionErrorAdditionalInfo(ArgumentMatcher matcherEntity, EntityId originatorId, TenantId tenantId, CustomerId customerId, UserId userId, String userName, ActionType actionType, - int cntTime, ArgumentMatcher matcherError, Object... additionalInfo) { - switch (additionalInfo.length) { + int cntTime, ArgumentMatcher matcherError, + List> matcherAdditionalInfos) { + switch (matcherAdditionalInfos.size()) { case 1: Mockito.verify(auditLogService, times(cntTime)) .logEntityAction(Mockito.eq(tenantId), @@ -363,7 +417,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.argThat(matcherEntity), Mockito.eq(actionType), Mockito.argThat(matcherError), - Mockito.eq(extractParameter(String.class, 0, additionalInfo))); + Mockito.argThat(matcherAdditionalInfos.get(0))); break; case 2: Mockito.verify(auditLogService, times(cntTime)) @@ -375,8 +429,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.argThat(matcherEntity), Mockito.eq(actionType), Mockito.argThat(matcherError), - Mockito.eq(extractParameter(String.class, 0, additionalInfo)), - Mockito.eq(extractParameter(String.class, 1, additionalInfo))); + Mockito.argThat(Mockito.eq(matcherAdditionalInfos.get(0))), + Mockito.argThat(Mockito.eq(matcherAdditionalInfos.get(1)))); case 3: Mockito.verify(auditLogService, times(cntTime)) .logEntityAction(Mockito.eq(tenantId), @@ -387,9 +441,9 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.argThat(matcherEntity), Mockito.eq(actionType), Mockito.argThat(matcherError), - Mockito.eq(extractParameter(String.class, 0, additionalInfo)), - Mockito.eq(extractParameter(String.class, 1, additionalInfo)), - Mockito.eq(extractParameter(String.class, 3, additionalInfo))); + Mockito.argThat(Mockito.eq(matcherAdditionalInfos.get(0))), + Mockito.argThat(Mockito.eq(matcherAdditionalInfos.get(1))), + Mockito.argThat(Mockito.eq(matcherAdditionalInfos.get(2)))); break; default: Mockito.verify(auditLogService, times(cntTime)) @@ -404,10 +458,18 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { } } - private T extractParameter(Class clazz, int index, Object... additionalInfo) { + private List> extractMatcherAdditionalInfo(Object... additionalInfos) { + List> matcherAdditionalInfos = new ArrayList<>(additionalInfos.length); + for (Object additionalInfo : additionalInfos) { + matcherAdditionalInfos.add(argument -> argument.equals(extractParameter(additionalInfo.getClass(), additionalInfo))); + } + return matcherAdditionalInfos; + } + + private T extractParameter(Class clazz, Object additionalInfo) { T result = null; - if (additionalInfo != null && additionalInfo.length > index) { - Object paramObject = additionalInfo[index]; + if (additionalInfo != null) { + Object paramObject = additionalInfo; if (clazz.isInstance(paramObject)) { result = clazz.cast(paramObject); } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java index 8852438b89..1321829683 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java @@ -27,12 +27,14 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; import org.thingsboard.common.util.ThingsBoardExecutors; 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.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceCredentialsId; @@ -44,6 +46,8 @@ 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.exception.DataValidationException; +import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; import org.thingsboard.server.dao.model.ModelConstants; import java.util.ArrayList; @@ -99,7 +103,6 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { doDelete("/api/tenant/" + savedTenant.getId().getId()) .andExpect(status().isOk()); - log.debug("afterTest done"); } @Test @@ -107,8 +110,17 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Device device = new Device(); device.setName("My device"); device.setType("default"); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + Device savedDevice = doPost("/api/device", device, Device.class); + Device oldDevice = new Device(savedDevice); + testNotifyEntityOneTimeMsgToEdgeServiceNever(savedDevice, savedDevice.getId(), savedDevice.getId(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ADDED); + testNotificationUpdateGatewayNever(); + Assert.assertNotNull(savedDevice); Assert.assertNotNull(savedDevice.getId()); Assert.assertTrue(savedDevice.getCreatedTime() > 0); @@ -127,9 +139,15 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Assert.assertNotNull(deviceCredentials.getCredentialsId()); Assert.assertEquals(20, deviceCredentials.getCredentialsId().length()); + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + savedDevice.setName("My new device"); doPost("/api/device", savedDevice, Device.class); + testNotifyEntityAllOneTime(savedDevice, savedDevice.getId(), savedDevice.getId(), savedTenant.getId(), + tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.UPDATED); + testNotificationUpdateGatewayOneTime(savedDevice, oldDevice); + Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId(), Device.class); Assert.assertEquals(foundDevice.getName(), savedDevice.getName()); } @@ -139,13 +157,35 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Device device = new Device(); device.setName(RandomStringUtils.randomAlphabetic(300)); device.setType("default"); - doPost("/api/device", device).andExpect(statusReason(containsString("length of name must be equal or less than 255"))); - device.setName("Normal Name"); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + + String msgError = "length of name must be equal or less than 255"; + doPost("/api/device", device).andExpect(statusReason(containsString(msgError))); + + testNotifyEntityEqualsOneTimeError(device, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); + testNotificationUpdateGatewayNever(); + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + + device.setTenantId(savedTenant.getId()); + msgError = "length of type must be equal or less than 255"; device.setType(RandomStringUtils.randomAlphabetic(300)); - doPost("/api/device", device).andExpect(statusReason(containsString("length of type must be equal or less than 255"))); + doPost("/api/device", device).andExpect(statusReason(containsString(msgError))); + + testNotifyEntityEqualsOneTimeError(device, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); + testNotificationUpdateGatewayNever(); + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + + msgError = "length of label must be equal or less than 255"; device.setType("Normal type"); device.setLabel(RandomStringUtils.randomAlphabetic(300)); - doPost("/api/device", device).andExpect(statusReason(containsString("length of label must be equal or less than 255"))); + doPost("/api/device", device).andExpect(statusReason(containsString(msgError))); + + testNotifyEntityEqualsOneTimeError(device, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); + testNotificationUpdateGatewayNever(); } @Test @@ -155,7 +195,22 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { device.setType("default"); Device savedDevice = doPost("/api/device", device, Device.class); loginDifferentTenant(); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + doPost("/api/device", savedDevice, Device.class, status().isNotFound()); + + testNotifyEntityNever(savedDevice.getId(), savedDevice); + testNotificationUpdateGatewayNever(); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + + doDelete("/api/device/" + savedDevice.getId().getId().toString()) + .andExpect(status().isNotFound()); + + testNotifyEntityNever(savedDevice.getId(), savedDevice); + testNotificationUpdateGatewayNever(); + deleteDifferentTenant(); } @@ -173,12 +228,24 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @Test public void testFindDeviceTypesByTenantId() throws Exception { List devices = new ArrayList<>(); - for (int i = 0; i < 3; i++) { + + int cntEntity = 3; + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + + for (int i = 0; i < cntEntity; i++) { Device device = new Device(); device.setName("My device B" + i); device.setType("typeB"); devices.add(doPost("/api/device", device, Device.class)); } + + testNotifyManyEntityManyTimeMsgToEdgeServiceNever(new Device(), new Device(), + savedTenant.getId(), + tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ADDED, cntEntity); + testNotificationUpdateGatewayNever(); + for (int i = 0; i < 7; i++) { Device device = new Device(); device.setName("My device C" + i); @@ -192,7 +259,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { devices.add(doPost("/api/device", device, Device.class)); } List deviceTypes = doGetTyped("/api/device/types", - new TypeReference>() { + new TypeReference<>() { }); Assert.assertNotNull(deviceTypes); @@ -211,10 +278,14 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { device.setType("default"); Device savedDevice = doPost("/api/device", device, Device.class); + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + doDelete("/api/device/" + savedDevice.getId().getId()) .andExpect(status().isOk()); -// gatewayNotificationsService.onDeviceDeleted(device); + testNotifyEntityOneTimeMsgToEdgeServiceNever(savedDevice, savedDevice.getId(), savedDevice.getId(), savedTenant.getId(), + tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.DELETED, savedDevice.getId().getId().toString()); + testNotificationDeleteGatewayOneTime(savedDevice); doGet("/api/device/" + savedDevice.getId().getId()) .andExpect(status().isNotFound()); @@ -224,17 +295,33 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { public void testSaveDeviceWithEmptyType() throws Exception { Device device = new Device(); device.setName("My device"); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + Device savedDevice = doPost("/api/device", device, Device.class); Assert.assertEquals("default", savedDevice.getType()); + + testNotifyEntityOneTimeMsgToEdgeServiceNever(savedDevice, savedDevice.getId(), savedDevice.getId(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ADDED); + testNotificationUpdateGatewayNever(); } @Test public void testSaveDeviceWithEmptyName() throws Exception { Device device = new Device(); device.setType("default"); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + + String msgError = "Device name should be specified"; doPost("/api/device", device) .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Device name should be specified"))); + .andExpect(statusReason(containsString(msgError))); + + testNotifyEntityEqualsOneTimeError(device, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); + testNotificationUpdateGatewayNever(); } @Test @@ -248,17 +335,33 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { customer.setTitle("My customer"); Customer savedCustomer = doPost("/api/customer", customer, Customer.class); + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + Device assignedDevice = doPost("/api/customer/" + savedCustomer.getId().getId() + "/device/" + savedDevice.getId().getId(), Device.class); Assert.assertEquals(savedCustomer.getId(), assignedDevice.getCustomerId()); + testNotifyEntityAllOneTime(assignedDevice, assignedDevice.getId(), assignedDevice.getId(), savedTenant.getId(), + savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ASSIGNED_TO_CUSTOMER, + assignedDevice.getId().getId().toString(), savedCustomer.getId().getId().toString(), + savedCustomer.getTitle()); + testNotificationUpdateGatewayNever(); + Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId(), Device.class); Assert.assertEquals(savedCustomer.getId(), foundDevice.getCustomerId()); + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + Device unassignedDevice = doDelete("/api/customer/device/" + savedDevice.getId().getId(), Device.class); Assert.assertEquals(ModelConstants.NULL_UUID, unassignedDevice.getCustomerId().getId()); + testNotifyEntityAllOneTime(unassignedDevice, unassignedDevice.getId(), unassignedDevice.getId(), savedTenant.getId(), + savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.UNASSIGNED_FROM_CUSTOMER, + unassignedDevice.getId().getId().toString(), savedCustomer.getId().getId().toString(), + savedCustomer.getTitle()); + testNotificationDeleteGatewayNever(); + foundDevice = doGet("/api/device/" + savedDevice.getId().getId(), Device.class); Assert.assertEquals(ModelConstants.NULL_UUID, foundDevice.getCustomerId().getId()); } @@ -269,9 +372,15 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { device.setName("My device"); device.setType("default"); Device savedDevice = doPost("/api/device", device, Device.class); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + doPost("/api/customer/" + Uuids.timeBased().toString() + "/device/" + savedDevice.getId().getId()) .andExpect(status().isNotFound()); + + testNotifyEntityNever(savedDevice.getId(), savedDevice); + testNotificationUpdateGatewayNever(); } @Test @@ -290,7 +399,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { tenantAdmin2.setFirstName("Joe"); tenantAdmin2.setLastName("Downs"); - tenantAdmin2 = createUserAndLogin(tenantAdmin2, "testPassword1"); + createUserAndLogin(tenantAdmin2, "testPassword1"); Customer customer = new Customer(); customer.setTitle("Different customer"); @@ -303,10 +412,15 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { device.setType("default"); Device savedDevice = doPost("/api/device", device, Device.class); + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + doPost("/api/customer/" + savedCustomer.getId().getId() + "/device/" + savedDevice.getId().getId()) .andExpect(status().isForbidden()); + testNotifyEntityNever(savedDevice.getId(), savedDevice); + testNotificationUpdateGatewayNever(); + loginSysAdmin(); doDelete("/api/tenant/" + savedTenant2.getId().getId()) @@ -335,9 +449,16 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId()); deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN); deviceCredentials.setCredentialsId("access_token"); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + doPost("/api/device/credentials", deviceCredentials) .andExpect(status().isOk()); + testNotifyEntityMsgToEdgePushMsgToCoreOneTime(savedDevice, savedDevice.getId(), savedDevice.getId(), savedTenant.getId(), + tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.CREDENTIALS_UPDATED, deviceCredentials); + testNotificationUpdateGatewayNever(); + DeviceCredentials foundDeviceCredentials = doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); @@ -347,8 +468,14 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @Test public void testSaveDeviceCredentialsWithEmptyDevice() throws Exception { DeviceCredentials deviceCredentials = new DeviceCredentials(); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + doPost("/api/device/credentials", deviceCredentials) .andExpect(status().isBadRequest()); + + testNotifyEntityNever(deviceCredentials.getDeviceId(), new Device()); + testNotificationUpdateGatewayNever(); } @Test @@ -360,9 +487,18 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { DeviceCredentials deviceCredentials = doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); deviceCredentials.setCredentialsType(null); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + + String msgError = "Device credentials type should be specified"; doPost("/api/device/credentials", deviceCredentials) .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Device credentials type should be specified"))); + .andExpect(statusReason(containsString(msgError))); + + testNotifyEntityIsNullOneTimeError(device, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.CREDENTIALS_UPDATED, + new DataValidationException(msgError), deviceCredentials); + testNotificationUpdateGatewayNever(); } @Test @@ -374,9 +510,18 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { DeviceCredentials deviceCredentials = doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); deviceCredentials.setCredentialsId(null); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + + String msgError = "Device credentials id should be specified"; doPost("/api/device/credentials", deviceCredentials) .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Device credentials id should be specified"))); + .andExpect(statusReason(containsString(msgError))); + + testNotifyEntityIsNullOneTimeError(device, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.CREDENTIALS_UPDATED, + new DeviceCredentialsValidationException(msgError), deviceCredentials); + testNotificationUpdateGatewayNever(); } @Test @@ -392,9 +537,18 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { newDeviceCredentials.setDeviceId(deviceCredentials.getDeviceId()); newDeviceCredentials.setCredentialsType(deviceCredentials.getCredentialsType()); newDeviceCredentials.setCredentialsId(deviceCredentials.getCredentialsId()); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + + String msgError = "Unable to update non-existent device credentials"; doPost("/api/device/credentials", newDeviceCredentials) .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Unable to update non-existent device credentials"))); + .andExpect(statusReason(containsString(msgError))); + + testNotifyEntityIsNullOneTimeError(device, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.CREDENTIALS_UPDATED, + new DeviceCredentialsValidationException(msgError), newDeviceCredentials); + testNotificationUpdateGatewayNever(); } @Test @@ -406,26 +560,40 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { DeviceCredentials deviceCredentials = doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); deviceCredentials.setDeviceId(new DeviceId(Uuids.timeBased())); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + doPost("/api/device/credentials", deviceCredentials) .andExpect(status().isNotFound()); + + testNotifyEntityNever(savedDevice.getId(), savedDevice); + testNotificationUpdateGatewayNever(); } @Test public void testFindTenantDevices() throws Exception { log.debug("testFindTenantDevices"); - futures = new ArrayList<>(178); - for (int i = 0; i < 178; i++) { + int cntEntity = 178; + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + + futures = new ArrayList<>(cntEntity); + for (int i = 0; i < cntEntity; i++) { Device device = new Device(); device.setName("Device" + i); device.setType("default"); futures.add(executor.submit(() -> doPost("/api/device", device, Device.class))); } - log.debug("await create devices"); + List devices = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS); - log.debug("start reading"); - List loadedDevices = new ArrayList<>(178); + testNotifyManyEntityManyTimeMsgToEdgeServiceNever(new Device(), new Device(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ADDED, cntEntity); + testNotificationUpdateGatewayNever(); + + List loadedDevices = new ArrayList<>(cntEntity); PageLink pageLink = new PageLink(23); do { pageData = doGetTypedWithPageLink("/api/tenant/devices?", @@ -437,11 +605,16 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { } } while (pageData.hasNext()); - log.debug("asserting"); assertThat(devices).containsExactlyInAnyOrderElementsOf(loadedDevices); - log.debug("delete devices async"); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + deleteEntitiesAsync("/api/device/", loadedDevices, executor).get(TIMEOUT, TimeUnit.SECONDS); - log.debug("done"); + + testNotifyManyEntityManyTimeMsgToEdgeServiceNeverAdditionalInfoAny(new Device(), new Device(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.DELETED, cntEntity, 1); + testNotificationUpdateGatewayNever(); } @Test @@ -606,9 +779,12 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { customer.setTitle("Test customer"); customer = doPost("/api/customer", customer, Customer.class); CustomerId customerId = customer.getId(); + int cntEntity = 128; - futures = new ArrayList<>(128); - for (int i = 0; i < 128; i++) { + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + + futures = new ArrayList<>(cntEntity); + for (int i = 0; i < cntEntity; i++) { Device device = new Device(); device.setName("Device" + i); device.setType("default"); @@ -620,7 +796,12 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { List devices = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS); - List loadedDevices = new ArrayList<>(128); + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new Device(), new Device(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ADDED, ActionType.ASSIGNED_TO_CUSTOMER, cntEntity, cntEntity * 2); + testNotificationUpdateGatewayNever(); + + List loadedDevices = new ArrayList<>(cntEntity); PageLink pageLink = new PageLink(23); do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId() + "/devices?", @@ -633,9 +814,14 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { assertThat(devices).containsExactlyInAnyOrderElementsOf(loadedDevices); - log.debug("delete devices async"); + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + deleteEntitiesAsync("/api/customer/device/", loadedDevices, executor).get(TIMEOUT, TimeUnit.SECONDS); - log.debug("done"); + + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(new Device(), new Device(), + savedTenant.getId(), customerId, tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.UNASSIGNED_FROM_CUSTOMER, ActionType.UNASSIGNED_FROM_CUSTOMER, cntEntity, cntEntity, 3); + testNotificationUpdateGatewayNever(); } @Test @@ -845,10 +1031,18 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { createUserAndLogin(user, "testPassword1"); login("tenant2@thingsboard.org", "testPassword1"); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + Device assignedDevice = doPost("/api/tenant/" + savedDifferentTenant.getId().getId() + "/device/" + savedDevice.getId().getId(), Device.class); doGet("/api/device/" + assignedDevice.getId().getId(), Device.class, status().isNotFound()); + testNotifyEntityOneTimeMsgToEdgeServiceNever(assignedDevice, assignedDevice.getId(), assignedDevice.getId(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ASSIGNED_TO_TENANT, savedDifferentTenant.getId().getId().toString(), savedDifferentTenant.getTitle()); + testNotificationUpdateGatewayNever(); + login("tenant9@thingsboard.org", "testPassword1"); Device foundDevice1 = doGet("/api/device/" + assignedDevice.getId().getId(), Device.class); @@ -871,17 +1065,32 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { device.setType("default"); Device savedDevice = doPost("/api/device", device, Device.class); + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + doPost("/api/edge/" + savedEdge.getId().getId() + "/device/" + savedDevice.getId().getId(), Device.class); + testNotifyEntityAllOneTime(savedDevice, savedDevice.getId(), savedDevice.getId(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ASSIGNED_TO_EDGE, + savedDevice.getId().getId().toString(), savedEdge.getId().getId().toString(), savedEdge.getName()); + testNotificationUpdateGatewayNever(); + pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/devices?", PAGE_DATA_DEVICE_TYPE_REF, new PageLink(100)); Assert.assertEquals(1, pageData.getData().size()); + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + doDelete("/api/edge/" + savedEdge.getId().getId() + "/device/" + savedDevice.getId().getId(), Device.class); + testNotifyEntityAllOneTime(savedDevice, savedDevice.getId(), savedDevice.getId(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.UNASSIGNED_FROM_EDGE, savedDevice.getId().getId().toString(), savedEdge.getId().getId().toString(), savedEdge.getName()); + testNotificationUpdateGatewayNever(); + pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/devices?", PAGE_DATA_DEVICE_TYPE_REF, new PageLink(100)); From cb8f11e210874c1343b263f2ea53fb80bb380a66 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 28 Jun 2022 11:42:09 +0300 Subject: [PATCH 03/20] refactoring: tests Device, Dashboard add int cntTimeEdge, int cntTimeRuleEngine --- .../device/DefaultTbDeviceService.java | 2 +- .../controller/AbstractNotifyEntityTest.java | 25 ++++++++++--------- .../BaseDashboardControllerTest.java | 2 +- .../controller/BaseDeviceControllerTest.java | 2 +- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java index 44cf43d765..2c7f4bcc95 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java @@ -116,7 +116,7 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T try { Device savedDevice = checkNotNull(deviceService.assignDeviceToCustomer(tenantId, deviceId, customerId)); notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, deviceId, customerId, savedDevice, - actionType, edgeTypeByActionType(actionType), user, true, deviceId.toString(), customerId.toString(), customer.getName()); + actionType, user, true, deviceId.toString(), customerId.toString(), customer.getName()); return savedDevice; } catch (Exception e) { diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java index 7757016cbf..ca2f8d7aaf 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java @@ -110,34 +110,35 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { } protected void testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(HasName entity, HasName originator, - TenantId tenantId, CustomerId customerId, UserId userId, String userName, - ActionType actionType, ActionType actionTypeEdge, int cntTime, int cntTimeEdge, Object... additionalInfo) { + TenantId tenantId, CustomerId customerId, UserId userId, String userName, + ActionType actionType, ActionType actionTypeEdge, + int cntTime, int cntTimeEdge, int cntTimeRuleEngine, Object... additionalInfo) { EntityId originatorId = createEntityId_NULL_UUID(originator); - testSendNotificationMsgToEdgeServiceTimeEntityEqAny(tenantId, actionTypeEdge, cntTime); + testSendNotificationMsgToEdgeServiceTimeEntityEqAny(tenantId, actionTypeEdge, cntTimeEdge); ArgumentMatcher matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, extractMatcherAdditionalInfo(additionalInfo)); - testPushMsgToRuleEngineTime(originatorId, tenantId, cntTimeEdge); + testPushMsgToRuleEngineTime(originatorId, tenantId, cntTimeRuleEngine); Mockito.reset(tbClusterService, auditLogService); } protected void testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(HasName entity, HasName originator, - TenantId tenantId, CustomerId customerId, UserId userId, String userName, - ActionType actionType, ActionType actionTypeEdge, int cntTime, int cntTimeEdge, int cntAdditionalInfo) { + TenantId tenantId, CustomerId customerId, UserId userId, String userName, + ActionType actionType, ActionType actionTypeEdge, int cntTime, int cntTimeEdge, int cntAdditionalInfo) { EntityId originatorId = createEntityId_NULL_UUID(originator); - testSendNotificationMsgToEdgeServiceTimeEntityEqAny(tenantId, actionTypeEdge, cntTime); + testSendNotificationMsgToEdgeServiceTimeEntityEqAny(tenantId, actionTypeEdge, cntTimeEdge); ArgumentMatcher matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, - cntAdditionalInfo); + cntAdditionalInfo); testPushMsgToRuleEngineTime(originatorId, tenantId, cntTimeEdge); Mockito.reset(tbClusterService, auditLogService); } protected void testNotifyManyEntityManyTimeMsgToEdgeServiceNeverAdditionalInfoAny(HasName entity, HasName originator, - TenantId tenantId, CustomerId customerId, UserId userId, String userName, - ActionType actionType, int cntTime, int cntAdditionalInfo) { + TenantId tenantId, CustomerId customerId, UserId userId, String userName, + ActionType actionType, int cntTime, int cntAdditionalInfo) { EntityId entityId = createEntityId_NULL_UUID(entity); EntityId originatorId = createEntityId_NULL_UUID(originator); testNotificationMsgToEdgeServiceNever(entityId); @@ -347,8 +348,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { } private void testLogEntityActionAdditionalInfoAny(ArgumentMatcher matcherEntity, ArgumentMatcher matcherOriginatorId, - TenantId tenantId, CustomerId customerId, UserId userId, String userName, - ActionType actionType, int cntTime, int cntAdditionalInfo) { + TenantId tenantId, CustomerId customerId, UserId userId, String userName, + ActionType actionType, int cntTime, int cntAdditionalInfo) { switch (cntAdditionalInfo) { case 1: Mockito.verify(auditLogService, times(cntTime)) diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java index f98d7f324a..78466427c8 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java @@ -432,7 +432,7 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new Dashboard(), new Dashboard(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.ADDED, ActionType.ASSIGNED_TO_CUSTOMER, cntEntity, cntEntity*2); + ActionType.ADDED, ActionType.ASSIGNED_TO_CUSTOMER, cntEntity, cntEntity, cntEntity*2); List loadedDashboards = new ArrayList<>(); PageLink pageLink = new PageLink(21); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java index 1321829683..6158494c28 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java @@ -798,7 +798,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new Device(), new Device(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.ADDED, ActionType.ASSIGNED_TO_CUSTOMER, cntEntity, cntEntity * 2); + ActionType.ADDED, ActionType.ASSIGNED_TO_CUSTOMER, cntEntity, cntEntity, cntEntity * 2); testNotificationUpdateGatewayNever(); List loadedDevices = new ArrayList<>(cntEntity); From d852043bfa72b58d4aa5bb4ca750703ce430eccf Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 28 Jun 2022 14:06:47 +0300 Subject: [PATCH 04/20] refactoring: tests testPushMsgToRuleEngineTime add matcherOriginatorId --- .../controller/AbstractNotifyEntityTest.java | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java index ca2f8d7aaf..066ef95642 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java @@ -63,7 +63,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { int cntTime = 1; testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); - testPushMsgToRuleEngineTime(originatorId, tenantId, cntTime); + ArgumentMatcher matcherOriginatorId = argument -> argument.equals(originatorId); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); Mockito.reset(tbClusterService, auditLogService); } @@ -73,7 +74,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { int cntTime = 1; testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); testLogEntityActionEntityEqClass(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); - testPushMsgToRuleEngineTime(originatorId, tenantId, cntTime); + ArgumentMatcher matcherOriginatorId = argument -> argument.equals(originatorId); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); Mockito.reset(tbClusterService, auditLogService); } @@ -91,7 +93,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { int cntTime = 1; testNotificationMsgToEdgeServiceNever(entityId); testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); - testPushMsgToRuleEngineTime(originatorId, tenantId, cntTime); + ArgumentMatcher matcherOriginatorId = argument -> argument.equals(originatorId); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); Mockito.reset(tbClusterService, auditLogService); } @@ -105,7 +108,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { ArgumentMatcher matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, extractMatcherAdditionalInfo(additionalInfo)); - testPushMsgToRuleEngineTime(originatorId, tenantId, cntTime); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); Mockito.reset(tbClusterService, auditLogService); } @@ -119,7 +122,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { ArgumentMatcher matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, extractMatcherAdditionalInfo(additionalInfo)); - testPushMsgToRuleEngineTime(originatorId, tenantId, cntTimeRuleEngine); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTimeRuleEngine); Mockito.reset(tbClusterService, auditLogService); } @@ -132,7 +135,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { ArgumentMatcher matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, cntAdditionalInfo); - testPushMsgToRuleEngineTime(originatorId, tenantId, cntTimeEdge); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTimeEdge); Mockito.reset(tbClusterService, auditLogService); } @@ -146,7 +149,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { ArgumentMatcher matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, cntAdditionalInfo); - testPushMsgToRuleEngineTime(originatorId, tenantId, cntTime); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); Mockito.reset(tbClusterService, auditLogService); } @@ -156,7 +159,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { int cntTime = 1; testNotificationMsgToEdgeServiceNever(entityId); testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); - testPushMsgToRuleEngineTime(originatorId, tenantId, cntTime); + ArgumentMatcher matcherOriginatorId = argument -> argument.equals(originatorId); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testBroadcastEntityStateChangeEventTime(entityId, tenantId, cntTime); Mockito.reset(tbClusterService, auditLogService); } @@ -245,13 +249,17 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any()); } - private void testPushMsgToRuleEngineTime(EntityId originatorId, TenantId tenantId, int cntTime) { - ArgumentMatcher matcherOriginatorId = cntTime == 1 ? argument -> argument.equals(originatorId) : - argument -> argument.getClass().equals(originatorId.getClass()); + private void testPushMsgToRuleEngineTime(ArgumentMatcher matcherOriginatorId, TenantId tenantId, int cntTime) { Mockito.verify(tbClusterService, times(cntTime)).pushMsgToRuleEngine(Mockito.eq(tenantId), Mockito.argThat(matcherOriginatorId), Mockito.any(TbMsg.class), Mockito.isNull()); } +// private void testPushMsgToRuleEngineOne(EntityId originatorId, TenantId tenantId) { +// ArgumentMatcher matcherOriginatorId = argument -> argument.equals(originatorId) ; +// Mockito.verify(tbClusterService, times(1)).pushMsgToRuleEngine(Mockito.eq(tenantId), +// Mockito.argThat(matcherOriginatorId), Mockito.any(TbMsg.class), Mockito.isNull()); +// } + private void testSendNotificationMsgToEdgeServiceTime(EntityId entityId, TenantId tenantId, ActionType actionType, int cntTime) { Mockito.verify(tbClusterService, times(cntTime)).sendNotificationMsgToEdge(Mockito.eq(tenantId), Mockito.any(), Mockito.eq(entityId), Mockito.any(), Mockito.isNull(), From 102787499d14d54cf8889cce391e06dcc1054855 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 28 Jun 2022 15:30:31 +0300 Subject: [PATCH 05/20] refactoring: tests Asset add .andExpect(statusReason(containsString(msgError)) --- .../controller/AbstractNotifyEntityTest.java | 6 - .../controller/BaseAssetControllerTest.java | 113 ++++++++++++------ .../controller/BaseDeviceControllerTest.java | 5 - 3 files changed, 74 insertions(+), 50 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java index 066ef95642..b99d2dadf3 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java @@ -254,12 +254,6 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.argThat(matcherOriginatorId), Mockito.any(TbMsg.class), Mockito.isNull()); } -// private void testPushMsgToRuleEngineOne(EntityId originatorId, TenantId tenantId) { -// ArgumentMatcher matcherOriginatorId = argument -> argument.equals(originatorId) ; -// Mockito.verify(tbClusterService, times(1)).pushMsgToRuleEngine(Mockito.eq(tenantId), -// Mockito.argThat(matcherOriginatorId), Mockito.any(TbMsg.class), Mockito.isNull()); -// } - private void testSendNotificationMsgToEdgeServiceTime(EntityId entityId, TenantId tenantId, ActionType actionType, int cntTime) { Mockito.verify(tbClusterService, times(cntTime)).sendNotificationMsgToEdge(Mockito.eq(tenantId), Mockito.any(), Mockito.eq(entityId), Mockito.any(), Mockito.isNull(), diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java index 0423d08edd..57cab67f5c 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -123,8 +123,9 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); String msgError = "length of name must be equal or less than 255"; - doPost("/api/asset", asset).andExpect(statusReason(containsString(msgError))); - + doPost("/api/asset", asset) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); testNotifyEntityEqualsOneTimeError(asset, savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); @@ -133,7 +134,9 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { asset.setName("Normal name"); asset.setType(RandomStringUtils.randomAlphabetic(300)); msgError = "length of type must be equal or less than 255"; - doPost("/api/asset", asset).andExpect(statusReason(containsString(msgError))); + doPost("/api/asset", asset) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); testNotifyEntityEqualsOneTimeError(asset, savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); @@ -142,7 +145,9 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { asset.setType("default"); asset.setLabel(RandomStringUtils.randomAlphabetic(300)); msgError = "length of label must be equal or less than 255"; - doPost("/api/asset", asset).andExpect(statusReason(containsString(msgError))); + doPost("/api/asset", asset) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); testNotifyEntityEqualsOneTimeError(asset, savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); @@ -239,8 +244,11 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.DELETED, savedAsset.getId().getId().toString()); + String assetIdStr = savedAsset.getId().getId().toString(); + String msgError = "Asset with id [" + assetIdStr + "] is not found"; doGet("/api/asset/" + savedAsset.getId().getId().toString()) - .andExpect(status().isNotFound()); + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgError))); } @Test @@ -264,12 +272,12 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - doDelete("/api/asset/" + savedAsset1.getId().getId().toString()) - .andExpect(status().isBadRequest()); - String msgError = "Can't delete asset that has entity views"; + doDelete("/api/asset/" + savedAsset1.getId().getId().toString()) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); - testNotifyEntityIsNullOneTimeError(savedAsset1, savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + testNotifyEntityIsNullOneTimeError(savedAsset1, savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.DELETED, new DataValidationException(msgError), savedAsset1.getId().getId().toString()); savedView.setEntityId(savedAsset2.getId()); @@ -279,8 +287,11 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { doDelete("/api/asset/" + savedAsset1.getId().getId().toString()) .andExpect(status().isOk()); - doGet("/api/asset/" + savedAsset1.getId().getId().toString()) - .andExpect(status().isNotFound()); + String assetIdStr = savedAsset1.getId().getId().toString(); + msgError = "Asset with id [" + assetIdStr + "] is not found"; + doGet("/api/asset/" + assetIdStr) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgError))); } @Test @@ -362,10 +373,12 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - String msgError = "Asset name should be specified"; - doPost("/api/customer/" + Uuids.timeBased().toString() + String customerIdStr = Uuids.timeBased().toString(); + String msgError = "Customer with id [" + customerIdStr + "] is not found"; + doPost("/api/customer/" + customerIdStr + "/asset/" + savedAsset.getId().getId().toString()) - .andExpect(status().isNotFound()); + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgError))); testNotifyEntityNever(asset.getId(), asset); } @@ -401,9 +414,11 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); + String msgError = "You don't have permission to perform this operation!"; doPost("/api/customer/" + savedCustomer.getId().getId().toString() + "/asset/" + savedAsset.getId().getId().toString()) - .andExpect(status().isForbidden()); + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgError))); testNotifyEntityNever(savedAsset.getId(), savedAsset); @@ -431,7 +446,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { PageData pageData = null; do { pageData = doGetTypedWithPageLink("/api/tenant/assets?", - new TypeReference>(){}, pageLink); + new TypeReference>() { + }, pageLink); loadedAssets.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -480,7 +496,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { PageData pageData = null; do { pageData = doGetTypedWithPageLink("/api/tenant/assets?", - new TypeReference>(){}, pageLink); + new TypeReference>() { + }, pageLink); loadedAssetsTitle1.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -496,7 +513,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, title2); do { pageData = doGetTypedWithPageLink("/api/tenant/assets?", - new TypeReference>(){}, pageLink); + new TypeReference>() { + }, pageLink); loadedAssetsTitle2.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -515,7 +533,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, title1); pageData = doGetTypedWithPageLink("/api/tenant/assets?", - new TypeReference>(){}, pageLink); + new TypeReference>() { + }, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -526,7 +545,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, title2); pageData = doGetTypedWithPageLink("/api/tenant/assets?", - new TypeReference>(){}, pageLink); + new TypeReference>() { + }, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); } @@ -563,7 +583,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { PageData pageData = null; do { pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&", - new TypeReference>(){}, pageLink, type1); + new TypeReference>() { + }, pageLink, type1); loadedAssetsType1.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -579,7 +600,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { pageLink = new PageLink(4); do { pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&", - new TypeReference>(){}, pageLink, type2); + new TypeReference>() { + }, pageLink, type2); loadedAssetsType2.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -598,7 +620,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { pageLink = new PageLink(4); pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&", - new TypeReference>(){}, pageLink, type1); + new TypeReference>() { + }, pageLink, type1); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -609,7 +632,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { pageLink = new PageLink(4); pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&", - new TypeReference>(){}, pageLink, type2); + new TypeReference>() { + }, pageLink, type2); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); } @@ -636,7 +660,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { PageData pageData = null; do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?", - new TypeReference>(){}, pageLink); + new TypeReference>() { + }, pageLink); loadedAssets.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -688,7 +713,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { PageData pageData = null; do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?", - new TypeReference>(){}, pageLink); + new TypeReference>() { + }, pageLink); loadedAssetsTitle1.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -704,7 +730,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, title2); do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?", - new TypeReference>(){}, pageLink); + new TypeReference>() { + }, pageLink); loadedAssetsTitle2.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -723,7 +750,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, title1); pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?", - new TypeReference>(){}, pageLink); + new TypeReference>() { + }, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -734,7 +762,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, title2); pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?", - new TypeReference>(){}, pageLink); + new TypeReference>() { + }, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); } @@ -780,7 +809,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { PageData pageData = null; do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&", - new TypeReference>(){}, pageLink, type1); + new TypeReference>() { + }, pageLink, type1); loadedAssetsType1.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -796,7 +826,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { pageLink = new PageLink(4); do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&", - new TypeReference>(){}, pageLink, type2); + new TypeReference>() { + }, pageLink, type2); loadedAssetsType2.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -815,7 +846,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { pageLink = new PageLink(4); pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&", - new TypeReference>(){}, pageLink, type1); + new TypeReference>() { + }, pageLink, type1); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -826,7 +858,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { pageLink = new PageLink(4); pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&", - new TypeReference>(){}, pageLink, type2); + new TypeReference>() { + }, pageLink, type2); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); } @@ -852,7 +885,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { PageData pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/assets?", - new TypeReference>() {}, new PageLink(100)); + new TypeReference>() { + }, new PageLink(100)); Assert.assertEquals(1, pageData.getData().size()); @@ -867,7 +901,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { ActionType.UNASSIGNED_FROM_EDGE, savedAsset.getId().getId().toString(), savedEdge.getId().getId().toString(), savedEdge.getName()); pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/assets?", - new TypeReference>() {}, new PageLink(100)); + new TypeReference>() { + }, new PageLink(100)); Assert.assertEquals(0, pageData.getData().size()); } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java index 6158494c28..b6dd8b267f 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java @@ -21,7 +21,6 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; -import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.RandomStringUtils; import org.junit.After; import org.junit.Assert; @@ -59,7 +58,6 @@ 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; -@Slf4j public abstract class BaseDeviceControllerTest extends AbstractControllerTest { static final TypeReference> PAGE_DATA_DEVICE_TYPE_REF = new TypeReference<>() { }; @@ -74,7 +72,6 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @Before public void beforeTest() throws Exception { - log.debug("beforeTest"); executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass())); loginSysAdmin(); @@ -96,7 +93,6 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @After public void afterTest() throws Exception { - log.debug("afterTest..."); executor.shutdownNow(); loginSysAdmin(); @@ -572,7 +568,6 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @Test public void testFindTenantDevices() throws Exception { - log.debug("testFindTenantDevices"); int cntEntity = 178; Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); From 68f0388723f135972b764c9c62e7ea2512370425 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Tue, 28 Jun 2022 14:54:38 +0200 Subject: [PATCH 06/20] added queue name and topic validation --- .../server/common/data/queue/Queue.java | 6 ++++++ .../dao/service/validator/QueueValidator.java | 14 ++++++++++++-- .../validator/TenantProfileDataValidator.java | 13 +++++++++++-- .../components/queue/queue-form.component.html | 3 +++ .../home/components/queue/queue-form.component.ts | 2 +- .../src/assets/locale/locale.constant-en_US.json | 1 + 6 files changed, 34 insertions(+), 5 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/queue/Queue.java b/common/data/src/main/java/org/thingsboard/server/common/data/queue/Queue.java index b4d65706a2..a6623be8a4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/queue/Queue.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/queue/Queue.java @@ -22,11 +22,17 @@ import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; import org.thingsboard.server.common.data.id.QueueId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration; +import org.thingsboard.server.common.data.validation.Length; +import org.thingsboard.server.common.data.validation.NoXss; @Data public class Queue extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId { private TenantId tenantId; + @NoXss + @Length(fieldName = "name") private String name; + @NoXss + @Length(fieldName = "topic") private String topic; private int pollInterval; private int partitions; diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueValidator.java index c8ce639d40..a1d0df43d5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueValidator.java @@ -29,6 +29,8 @@ import org.thingsboard.server.dao.queue.QueueDao; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; +import java.util.regex.Pattern; + @Component public class QueueValidator extends DataValidator { @@ -38,6 +40,8 @@ public class QueueValidator extends DataValidator { @Autowired private TbTenantProfileCache tenantProfileCache; + private final Pattern queueTopicPattern = Pattern.compile("^[a-zA-Z0-9_.\\-]+$"); + @Override protected void validateCreate(TenantId tenantId, Queue queue) { if (queueDao.findQueueByTenantIdAndName(tenantId, queue.getName()) != null) { @@ -76,8 +80,14 @@ public class QueueValidator extends DataValidator { if (StringUtils.isEmpty(queue.getName())) { throw new DataValidationException("Queue name should be specified!"); } - if (StringUtils.isBlank(queue.getTopic())) { - throw new DataValidationException("Queue topic should be non empty and without spaces!"); + if (!queueTopicPattern.matcher(queue.getName()).matches()) { + throw new DataValidationException("Queue name contains a character other than ASCII alphanumerics, '.', '_' and '-'!"); + } + if (StringUtils.isEmpty(queue.getTopic())) { + throw new DataValidationException("Queue topic should be specified!"); + } + if (!queueTopicPattern.matcher(queue.getTopic()).matches()) { + throw new DataValidationException("Queue topic contains a character other than ASCII alphanumerics, '.', '_' and '-'!"); } if (queue.getPollInterval() < 1) { throw new DataValidationException("Queue poll interval should be more then 0!"); diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java index ce15ec9e88..bfca262abf 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java @@ -34,6 +34,7 @@ import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.regex.Pattern; @Component public class TenantProfileDataValidator extends DataValidator { @@ -45,6 +46,8 @@ public class TenantProfileDataValidator extends DataValidator { @Lazy private TenantProfileService tenantProfileService; + private final Pattern queueTopicPattern = Pattern.compile("^[a-zA-Z0-9_.\\-]+$"); + @Override protected void validateDataImpl(TenantId tenantId, TenantProfile tenantProfile) { if (StringUtils.isEmpty(tenantProfile.getName())) { @@ -110,8 +113,14 @@ public class TenantProfileDataValidator extends DataValidator { if (StringUtils.isEmpty(queue.getName())) { throw new DataValidationException("Queue name should be specified!"); } - if (StringUtils.isBlank(queue.getTopic())) { - throw new DataValidationException("Queue topic should be non empty and without spaces!"); + if (!queueTopicPattern.matcher(queue.getName()).matches()) { + throw new DataValidationException("Queue name contains a character other than ASCII alphanumerics, '.', '_' and '-'!"); + } + if (StringUtils.isEmpty(queue.getTopic())) { + throw new DataValidationException("Queue topic should be specified!"); + } + if (!queueTopicPattern.matcher(queue.getTopic()).matches()) { + throw new DataValidationException("Queue topic contains a character other than ASCII alphanumerics, '.', '_' and '-'!"); } if (queue.getPollInterval() < 1) { throw new DataValidationException("Queue poll interval should be more then 0!"); diff --git a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html index 6c34e0111f..6e291e9e23 100644 --- a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html +++ b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html @@ -26,6 +26,9 @@ {{ 'queue.name-unique' | translate }} + + {{ 'queue.name-pattern' | translate }} + diff --git a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts index b84dfc9985..30df2ff7ae 100644 --- a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts +++ b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts @@ -99,7 +99,7 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, OnDestr ngOnInit() { this.queueFormGroup = this.fb.group( { - name: ['', [Validators.required]], + name: ['', [Validators.required, Validators.pattern(/^[a-zA-Z0-9_.\-]+$/)]], pollInterval: [25, [Validators.min(1), Validators.required]], partitions: [10, [Validators.min(1), Validators.required]], consumerPerPartition: [false, []], diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 0286b5f9d2..747a12c336 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2928,6 +2928,7 @@ "name": "Name", "name-required": "Queue name is required!", "name-unique": "Queue name is not unique!", + "name-pattern": "Queue name contains a character other than ASCII alphanumerics, '.', '_' and '-'!", "queue-required": "Queue is required!", "topic-required": "Queue topic is required!", "poll-interval-required": "Poll interval is required!", From c21a9c116493bf532e5c6bb4616219e1b01c644d Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Tue, 28 Jun 2022 15:58:15 +0200 Subject: [PATCH 07/20] added queue validation tests --- .../dao/service/BaseQueueServiceTest.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseQueueServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseQueueServiceTest.java index 577193ff94..428a21299d 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseQueueServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseQueueServiceTest.java @@ -152,6 +152,20 @@ public abstract class BaseQueueServiceTest extends AbstractServiceTest { queueService.saveQueue(queue); } + @Test(expected = DataValidationException.class) + public void testSaveQueueWithInvalidName() { + Queue queue = new Queue(); + queue.setTenantId(tenantId); + queue.setName("Test 1"); + queue.setTopic("tb_rule_engine.test"); + queue.setPollInterval(25); + queue.setPartitions(1); + queue.setPackProcessingTimeout(2000); + queue.setSubmitStrategy(createTestSubmitStrategy()); + queue.setProcessingStrategy(createTestProcessingStrategy()); + queueService.saveQueue(queue); + } + @Test(expected = DataValidationException.class) public void testSaveQueueWithEmptyTopic() { Queue queue = new Queue(); @@ -165,6 +179,20 @@ public abstract class BaseQueueServiceTest extends AbstractServiceTest { queueService.saveQueue(queue); } + @Test(expected = DataValidationException.class) + public void testSaveQueueWithInvalidTopic() { + Queue queue = new Queue(); + queue.setTenantId(tenantId); + queue.setName("Test"); + queue.setTopic("tb rule engine test"); + queue.setPollInterval(25); + queue.setPartitions(1); + queue.setPackProcessingTimeout(2000); + queue.setSubmitStrategy(createTestSubmitStrategy()); + queue.setProcessingStrategy(createTestProcessingStrategy()); + queueService.saveQueue(queue); + } + @Test(expected = DataValidationException.class) public void testSaveQueueWithEmptyPollInterval() { Queue queue = new Queue(); From 07d48b078124d98cef8a7523270e45943e2ae76a Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 28 Jun 2022 18:27:48 +0300 Subject: [PATCH 08/20] docker-compose checkFolders: create and set owner --- docker/compose-utils.sh | 74 +++++++++++++++++++++++++++++ docker/docker-check-log-folders.sh | 21 ++++++++ docker/docker-create-log-folders.sh | 39 ++------------- docker/docker-install-tb.sh | 2 + docker/docker-start-services.sh | 2 + docker/docker-upgrade-tb.sh | 2 + 6 files changed, 104 insertions(+), 36 deletions(-) create mode 100755 docker/docker-check-log-folders.sh diff --git a/docker/compose-utils.sh b/docker/compose-utils.sh index 28601afa3c..bbd8357a87 100755 --- a/docker/compose-utils.sh +++ b/docker/compose-utils.sh @@ -121,3 +121,77 @@ function additionalStartupServices() { echo $ADDITIONAL_STARTUP_SERVICES } + +function permissionList() { + PERMISSION_LIST=" + 799 799 tb-node/log + 799 799 tb-transports/coap/log + 799 799 tb-transports/lwm2m/log + 799 799 tb-transports/http/log + 799 799 tb-transports/mqtt/log + 799 799 tb-transports/snmp/log + 799 799 tb-transports/coap/log + 799 799 tb-vc-executor/log + 999 999 tb-node/postgres + " + + source .env + + if [ "$DATABASE" = "hybrid" ]; then + PERMISSION_LIST="$PERMISSION_LIST + 999 999 tb-node/cassandra + " + fi + + CACHE="${CACHE:-redis}" + case $CACHE in + redis) + PERMISSION_LIST="$PERMISSION_LIST + 1001 1001 tb-node/redis-data + " + ;; + redis-cluster) + PERMISSION_LIST="$PERMISSION_LIST + 1001 1001 tb-node/redis-cluster-data-0 + 1001 1001 tb-node/redis-cluster-data-1 + 1001 1001 tb-node/redis-cluster-data-2 + 1001 1001 tb-node/redis-cluster-data-3 + 1001 1001 tb-node/redis-cluster-data-4 + 1001 1001 tb-node/redis-cluster-data-5 + " + ;; + *) + echo "Unknown CACHE value specified in the .env file: '${CACHE}'. Should be either 'redis' or 'redis-cluster'." >&2 + exit 1 + esac + + echo "$PERMISSION_LIST" +} + +function checkFolders() { + EXIT_CODE=0 + PERMISSION_LIST=$(permissionList) || exit $? + set -e + while read -r USR GRP DIR + do + if [ -z "$DIR" ]; then # skip empty lines + continue + fi + MESSAGE="Checking user ${USR} group ${GRP} dir ${DIR} " + if [[ -d "$DIR" ]] && + [[ $(ls -ldn "$DIR" | awk '{print $3}') -eq "$USR" ]] && + [[ $(ls -ldn "$DIR" | awk '{print $4}') -eq "$GRP" ]] + then + MESSAGE="$MESSAGE OK" + else + if [ "$1" = "--create" ]; then + echo "Create and chown: user ${USR} group ${GRP} dir ${DIR}" + mkdir -p "$DIR" && sudo chown -R "$USR":"$GRP" "$DIR" + else + echo "$MESSAGE FAILED" + EXIT_CODE=1 + fi + fi + done < <(echo "$PERMISSION_LIST") + return $EXIT_CODE +} diff --git a/docker/docker-check-log-folders.sh b/docker/docker-check-log-folders.sh new file mode 100755 index 0000000000..66e7ea394c --- /dev/null +++ b/docker/docker-check-log-folders.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# +# 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. +# + +set -e +source compose-utils.sh +checkFolders || exit $? +echo "OK" diff --git a/docker/docker-create-log-folders.sh b/docker/docker-create-log-folders.sh index 6224b7040b..54a74f4812 100755 --- a/docker/docker-create-log-folders.sh +++ b/docker/docker-create-log-folders.sh @@ -15,39 +15,6 @@ # limitations under the License. # -mkdir -p tb-node/log && sudo chown -R 799:799 tb-node/log - -mkdir -p tb-transports/coap/log && sudo chown -R 799:799 tb-transports/coap/log - -mkdir -p tb-transports/lwm2m/log && sudo chown -R 799:799 tb-transports/lwm2m/log - -mkdir -p tb-transports/http/log && sudo chown -R 799:799 tb-transports/http/log - -mkdir -p tb-transports/mqtt/log && sudo chown -R 799:799 tb-transports/mqtt/log - -mkdir -p tb-transports/snmp/log && sudo chown -R 799:799 tb-transports/snmp/log - -mkdir -p tb-vc-executor/log && sudo chown -R 799:799 tb-vc-executor/log - -mkdir -p tb-node/postgres && sudo chown -R 999:999 tb-node/postgres - -mkdir -p tb-node/cassandra && sudo chown -R 999:999 tb-node/cassandra - -source .env -CACHE="${CACHE:-redis}" -case $CACHE in - redis) - mkdir -p tb-node/redis-data && sudo chown -R 1001:1001 tb-node/redis-data - ;; - redis-cluster) - mkdir -p tb-node/redis-cluster-data-0 && sudo chown -R 1001:1001 tb-node/redis-cluster-data-0 - mkdir -p tb-node/redis-cluster-data-1 && sudo chown -R 1001:1001 tb-node/redis-cluster-data-1 - mkdir -p tb-node/redis-cluster-data-2 && sudo chown -R 1001:1001 tb-node/redis-cluster-data-2 - mkdir -p tb-node/redis-cluster-data-3 && sudo chown -R 1001:1001 tb-node/redis-cluster-data-3 - mkdir -p tb-node/redis-cluster-data-4 && sudo chown -R 1001:1001 tb-node/redis-cluster-data-4 - mkdir -p tb-node/redis-cluster-data-5 && sudo chown -R 1001:1001 tb-node/redis-cluster-data-5 - ;; - *) - echo "Unknown CACHE value specified in the .env file: '${CACHE}'. Should be either 'redis' or 'redis-cluster'." >&2 - exit 1 -esac \ No newline at end of file +set -e +source compose-utils.sh +checkFolders --create diff --git a/docker/docker-install-tb.sh b/docker/docker-install-tb.sh index 9129c72796..f86b82acc8 100755 --- a/docker/docker-install-tb.sh +++ b/docker/docker-install-tb.sh @@ -41,6 +41,8 @@ set -e source compose-utils.sh +checkFolders --create || exit $? + ADDITIONAL_COMPOSE_QUEUE_ARGS=$(additionalComposeQueueArgs) || exit $? ADDITIONAL_COMPOSE_ARGS=$(additionalComposeArgs) || exit $? diff --git a/docker/docker-start-services.sh b/docker/docker-start-services.sh index 9e5d08add9..4762f1a040 100755 --- a/docker/docker-start-services.sh +++ b/docker/docker-start-services.sh @@ -19,6 +19,8 @@ set -e source compose-utils.sh +checkFolders --create || exit $? + ADDITIONAL_COMPOSE_QUEUE_ARGS=$(additionalComposeQueueArgs) || exit $? ADDITIONAL_COMPOSE_ARGS=$(additionalComposeArgs) || exit $? diff --git a/docker/docker-upgrade-tb.sh b/docker/docker-upgrade-tb.sh index 260e14cf8b..d0c82f6794 100755 --- a/docker/docker-upgrade-tb.sh +++ b/docker/docker-upgrade-tb.sh @@ -40,6 +40,8 @@ set -e source compose-utils.sh +checkFolders --create || exit $? + ADDITIONAL_COMPOSE_QUEUE_ARGS=$(additionalComposeQueueArgs) || exit $? ADDITIONAL_COMPOSE_ARGS=$(additionalComposeArgs) || exit $? From 2841d6b6f25d40ac1efab0b388f9fb0bb370ba23 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 28 Jun 2022 18:48:37 +0300 Subject: [PATCH 09/20] compose-utils: whitespace removed --- docker/compose-utils.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/compose-utils.sh b/docker/compose-utils.sh index bbd8357a87..f45339eb92 100755 --- a/docker/compose-utils.sh +++ b/docker/compose-utils.sh @@ -177,7 +177,7 @@ function checkFolders() { if [ -z "$DIR" ]; then # skip empty lines continue fi - MESSAGE="Checking user ${USR} group ${GRP} dir ${DIR} " + MESSAGE="Checking user ${USR} group ${GRP} dir ${DIR}" if [[ -d "$DIR" ]] && [[ $(ls -ldn "$DIR" | awk '{print $3}') -eq "$USR" ]] && [[ $(ls -ldn "$DIR" | awk '{print $4}') -eq "$GRP" ]] From 2f9537449179f1836f30c50efda9c6c9bdae3823 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 28 Jun 2022 19:02:43 +0300 Subject: [PATCH 10/20] docker-compose checkFolders: refactored for easier merge with docker-compose project --- docker/docker-install-tb.sh | 4 ++-- docker/docker-start-services.sh | 4 ++-- docker/docker-upgrade-tb.sh | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docker/docker-install-tb.sh b/docker/docker-install-tb.sh index f86b82acc8..628b16e6ee 100755 --- a/docker/docker-install-tb.sh +++ b/docker/docker-install-tb.sh @@ -41,8 +41,6 @@ set -e source compose-utils.sh -checkFolders --create || exit $? - ADDITIONAL_COMPOSE_QUEUE_ARGS=$(additionalComposeQueueArgs) || exit $? ADDITIONAL_COMPOSE_ARGS=$(additionalComposeArgs) || exit $? @@ -51,6 +49,8 @@ ADDITIONAL_CACHE_ARGS=$(additionalComposeCacheArgs) || exit $? ADDITIONAL_STARTUP_SERVICES=$(additionalStartupServices) || exit $? +checkFolders --create || exit $? + if [ ! -z "${ADDITIONAL_STARTUP_SERVICES// }" ]; then docker-compose \ -f docker-compose.yml $ADDITIONAL_CACHE_ARGS $ADDITIONAL_COMPOSE_ARGS $ADDITIONAL_COMPOSE_QUEUE_ARGS \ diff --git a/docker/docker-start-services.sh b/docker/docker-start-services.sh index 4762f1a040..1a06946308 100755 --- a/docker/docker-start-services.sh +++ b/docker/docker-start-services.sh @@ -19,8 +19,6 @@ set -e source compose-utils.sh -checkFolders --create || exit $? - ADDITIONAL_COMPOSE_QUEUE_ARGS=$(additionalComposeQueueArgs) || exit $? ADDITIONAL_COMPOSE_ARGS=$(additionalComposeArgs) || exit $? @@ -29,6 +27,8 @@ ADDITIONAL_CACHE_ARGS=$(additionalComposeCacheArgs) || exit $? ADDITIONAL_COMPOSE_MONITORING_ARGS=$(additionalComposeMonitoringArgs) || exit $? +checkFolders --create || exit $? + docker-compose \ -f docker-compose.yml $ADDITIONAL_CACHE_ARGS $ADDITIONAL_COMPOSE_ARGS $ADDITIONAL_COMPOSE_QUEUE_ARGS $ADDITIONAL_COMPOSE_MONITORING_ARGS \ up -d diff --git a/docker/docker-upgrade-tb.sh b/docker/docker-upgrade-tb.sh index d0c82f6794..87dff9da15 100755 --- a/docker/docker-upgrade-tb.sh +++ b/docker/docker-upgrade-tb.sh @@ -40,8 +40,6 @@ set -e source compose-utils.sh -checkFolders --create || exit $? - ADDITIONAL_COMPOSE_QUEUE_ARGS=$(additionalComposeQueueArgs) || exit $? ADDITIONAL_COMPOSE_ARGS=$(additionalComposeArgs) || exit $? @@ -50,6 +48,8 @@ ADDITIONAL_CACHE_ARGS=$(additionalComposeCacheArgs) || exit $? ADDITIONAL_STARTUP_SERVICES=$(additionalStartupServices) || exit $? +checkFolders --create || exit $? + docker-compose \ -f docker-compose.yml $ADDITIONAL_CACHE_ARGS $ADDITIONAL_COMPOSE_ARGS $ADDITIONAL_COMPOSE_QUEUE_ARGS \ pull \ From ea04b5607666af03f55ee9dde3f41f59f5e9bb9d Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 28 Jun 2022 21:21:45 +0300 Subject: [PATCH 11/20] refactoring: tests comments1 --- .../entitiy/DefaultTbNotificationEntityService.java | 10 +++++----- .../service/entitiy/TbNotificationEntityService.java | 4 ++-- .../service/entitiy/device/DefaultTbDeviceService.java | 10 ++++------ .../server/controller/BaseAssetControllerTest.java | 8 ++++---- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java index 47e4154dae..89d04a99de 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java @@ -179,16 +179,16 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS @Override public void notifyUpdateDeviceCredentials(TenantId tenantId, DeviceId deviceId, CustomerId customerId, Device device, - DeviceCredentials deviceCredentials, ActionType actionType, User user) { + DeviceCredentials deviceCredentials, User user) { tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(tenantId, deviceCredentials.getDeviceId(), deviceCredentials), null); - sendEntityNotificationMsg(tenantId, deviceId, edgeTypeByActionType(actionType)); - logEntityAction(tenantId, deviceId, device, customerId, actionType, user, deviceCredentials); + sendEntityNotificationMsg(tenantId, deviceId, EdgeEventActionType.CREDENTIALS_UPDATED); + logEntityAction(tenantId, deviceId, device, customerId, ActionType.CREDENTIALS_UPDATED, user, deviceCredentials); } @Override public void notifyAssignDeviceToTenant(TenantId tenantId, TenantId newTenantId, DeviceId deviceId, CustomerId customerId, - Device device, Tenant tenant, ActionType actionType, User user, Object... additionalInfo) { - logEntityAction(tenantId, deviceId, device, customerId, actionType, user, additionalInfo); + Device device, Tenant tenant, User user, Object... additionalInfo) { + logEntityAction(tenantId, deviceId, device, customerId, ActionType.ASSIGNED_TO_TENANT, user, additionalInfo); pushAssignedFromNotification(tenant, newTenantId, device); } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java index 3867693133..7e929254a6 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java @@ -92,10 +92,10 @@ public interface TbNotificationEntityService { List relatedEdgeIds, User user, Object... additionalInfo); void notifyUpdateDeviceCredentials(TenantId tenantId, DeviceId deviceId, CustomerId customerId, Device device, - DeviceCredentials deviceCredentials, ActionType actionType, User user); + DeviceCredentials deviceCredentials, User user); void notifyAssignDeviceToTenant(TenantId tenantId, TenantId newTenantId, DeviceId deviceId, CustomerId customerId, - Device device, Tenant tenant, ActionType actionType, User user, Object... additionalInfo); + Device device, Tenant tenant, User user, Object... additionalInfo); void notifyEdge(TenantId tenantId, EdgeId edgeId, CustomerId customerId, Edge edge, ActionType actionType, User user, Object... additionalInfo); diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java index 2c7f4bcc95..959e6520b1 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java @@ -185,14 +185,13 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T public DeviceCredentials updateDeviceCredentials(Device device, DeviceCredentials deviceCredentials, User user) throws ThingsboardException { TenantId tenantId = device.getTenantId(); DeviceId deviceId = device.getId(); - ActionType actionType = ActionType.CREDENTIALS_UPDATED; try { DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(tenantId, deviceCredentials)); - notificationEntityService.notifyUpdateDeviceCredentials(tenantId, deviceId, device.getCustomerId(), device, result, actionType, user); + notificationEntityService.notifyUpdateDeviceCredentials(tenantId, deviceId, device.getCustomerId(), device, result, user); return result; } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DEVICE), - actionType, user, e, deviceCredentials); + ActionType.CREDENTIALS_UPDATED, user, e, deviceCredentials); throw e; } } @@ -231,18 +230,17 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T TenantId tenantId = device.getTenantId(); TenantId newTenantId = newTenant.getId(); DeviceId deviceId = device.getId(); - ActionType actionType = ActionType.ASSIGNED_TO_TENANT; try { Tenant tenant = tenantService.findTenantById(tenantId); Device assignedDevice = deviceService.assignDeviceToTenant(newTenantId, device); notificationEntityService.notifyAssignDeviceToTenant(tenantId, newTenantId, deviceId, - assignedDevice.getCustomerId(), assignedDevice, tenant, actionType, user, newTenantId.toString(), newTenant.getName()); + assignedDevice.getCustomerId(), assignedDevice, tenant, user, newTenantId.toString(), newTenant.getName()); return assignedDevice; } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DEVICE), - actionType, user, e, deviceId.toString()); + ActionType.ASSIGNED_TO_TENANT, user, e, deviceId.toString()); throw e; } } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java index 57cab67f5c..b784b3a596 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. From b658b4956a4769de13172981aeb64f5bf1cee332 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 28 Jun 2022 22:18:58 +0300 Subject: [PATCH 12/20] refactoring: tests Alarm, Customer add .andExpect( --- .../controller/AbstractNotifyEntityTest.java | 2 ++ .../controller/BaseAlarmControllerTest.java | 36 ++++++++++++++----- .../BaseCustomerControllerTest.java | 34 +++++++++++++----- 3 files changed, 54 insertions(+), 18 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java index b99d2dadf3..197d1dceeb 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java @@ -57,6 +57,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { @SpyBean protected GatewayNotificationsService gatewayNotificationsService; + protected final String msgErrorPermission = "You don't have permission to perform this operation!"; + protected void testNotifyEntityAllOneTime(HasName entity, EntityId entityId, EntityId originatorId, TenantId tenantId, CustomerId customerId, UserId userId, String userName, ActionType actionType, Object... additionalInfo) { diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java index 95c90dea30..9eab1e56b3 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java @@ -36,6 +36,7 @@ import org.thingsboard.server.common.data.page.PageData; import java.util.LinkedList; import java.util.List; +import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @Slf4j @@ -132,7 +133,9 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - doPost("/api/alarm", alarm).andExpect(status().isForbidden()); + doPost("/api/alarm", alarm) + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); testNotifyEntityNever(alarm.getId(), alarm); } @@ -147,7 +150,9 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - doPost("/api/alarm", alarm).andExpect(status().isForbidden()); + doPost("/api/alarm", alarm) + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); testNotifyEntityNever(alarm.getId(), alarm); } @@ -187,7 +192,9 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isForbidden()); + doDelete("/api/alarm/" + alarm.getId()) + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); testNotifyEntityNever(alarm.getId(), alarm); } @@ -201,7 +208,9 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isForbidden()); + doDelete("/api/alarm/" + alarm.getId()) + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); testNotifyEntityNever(alarm.getId(), alarm); } @@ -265,7 +274,9 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - doPost("/api/alarm/" + alarm.getId() + "/clear").andExpect(status().isForbidden()); + doPost("/api/alarm/" + alarm.getId() + "/clear") + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); testNotifyEntityNever(alarm.getId(), alarm); } @@ -279,7 +290,9 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - doPost("/api/alarm/" + alarm.getId() + "/clear").andExpect(status().isForbidden()); + doPost("/api/alarm/" + alarm.getId() + "/clear") + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); testNotifyEntityNever(alarm.getId(), alarm); } @@ -293,7 +306,9 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - doPost("/api/alarm/" + alarm.getId() + "/ack").andExpect(status().isForbidden()); + doPost("/api/alarm/" + alarm.getId() + "/ack") + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); testNotifyEntityNever(alarm.getId(), alarm); } @@ -307,7 +322,9 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - doPost("/api/alarm/" + alarm.getId() + "/ack").andExpect(status().isForbidden()); + doPost("/api/alarm/" + alarm.getId() + "/ack").andExpect(status() + .isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); } @Test @@ -355,7 +372,8 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { loginDifferentCustomer(); doGet("/api/alarm/" + EntityType.DEVICE + "/" + customerDevice.getUuidId() + "?page=0&pageSize=" + size) - .andExpect(status().isForbidden()); + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); } @Test diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java index bbba51bd60..1b1d269c79 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java @@ -127,7 +127,9 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest Mockito.reset(tbClusterService, auditLogService); String msgError = "length of title must be equal or less than 255"; - doPost("/api/customer", customer).andExpect(statusReason(containsString(msgError))); + doPost("/api/customer", customer) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); customer.setTenantId(savedTenant.getId()); testNotifyEntityEqualsOneTimeError(customer,savedTenant.getId(), @@ -137,7 +139,9 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest customer.setTitle("Normal title"); customer.setCity(RandomStringUtils.randomAlphabetic(300)); msgError = "length of city must be equal or less than 255"; - doPost("/api/customer", customer).andExpect(statusReason(containsString(msgError))); + doPost("/api/customer", customer) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); testNotifyEntityEqualsOneTimeError(customer,savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); @@ -146,7 +150,9 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest customer.setCity("Normal city"); customer.setCountry(RandomStringUtils.randomAlphabetic(300)); msgError = "length of country must be equal or less than 255"; - doPost("/api/customer", customer).andExpect(statusReason(containsString(msgError))); + doPost("/api/customer", customer) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); testNotifyEntityEqualsOneTimeError(customer,savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); @@ -155,7 +161,9 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest customer.setCountry("Ukraine"); customer.setPhone(RandomStringUtils.randomAlphabetic(300)); msgError = "length of phone must be equal or less than 255"; - doPost("/api/customer", customer).andExpect(statusReason(containsString(msgError))); + doPost("/api/customer", customer) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); testNotifyEntityEqualsOneTimeError(customer,savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); @@ -164,7 +172,9 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest customer.setPhone("+3892555554512"); customer.setState(RandomStringUtils.randomAlphabetic(300)); msgError = "length of state must be equal or less than 255"; - doPost("/api/customer", customer).andExpect(statusReason(containsString(msgError))); + doPost("/api/customer", customer) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); testNotifyEntityEqualsOneTimeError(customer,savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); @@ -173,7 +183,9 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest customer.setState("Normal state"); customer.setZip(RandomStringUtils.randomAlphabetic(300)); msgError = "length of zip or postal code must be equal or less than 255"; - doPost("/api/customer", customer).andExpect(statusReason(containsString(msgError))); + doPost("/api/customer", customer) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); testNotifyEntityEqualsOneTimeError(customer,savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); @@ -195,7 +207,8 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest testNotifyEntityNever(savedCustomer.getId(), savedCustomer); doDelete("/api/customer/" + savedCustomer.getId().getId().toString()) - .andExpect(status().isForbidden()); + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); testNotifyEntityNever(savedCustomer.getId(), savedCustomer); @@ -241,8 +254,11 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest savedCustomer.getId(), savedCustomer.getTenantId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.DELETED, savedCustomer.getId().getId().toString()); - doGet("/api/customer/" + savedCustomer.getId().getId().toString()) - .andExpect(status().isNotFound()); + String customerIdStr = savedCustomer.getId().getId().toString(); + String msgError = "Customer with id [" + customerIdStr + "] is not found"; + doGet("/api/customer/" + customerIdStr) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgError))); } @Test From 395d4f49004c57f471fdaa727b26cbf518e1d961 Mon Sep 17 00:00:00 2001 From: fe-dev Date: Wed, 29 Jun 2022 09:20:40 +0300 Subject: [PATCH 13/20] UI: Bug fix validation queue form --- .../app/modules/home/components/queue/queue-form.component.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts index b84dfc9985..c1aecea4b2 100644 --- a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts +++ b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts @@ -209,6 +209,7 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, OnDestr } else { batchSizeField.patchValue(null, {emitEvent: false}); batchSizeField.clearValidators(); + batchSizeField.updateValueAndValidity(); this.hideBatchSize = false; } } From 50abe7e00d4e68b869c4554d914cac22ef2af020 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 29 Jun 2022 11:38:32 +0300 Subject: [PATCH 14/20] refactoring: tests Dashboard add .andExpect(. Commits2 --- .../DefaultTbNotificationEntityService.java | 2 -- .../controller/AbstractNotifyEntityTest.java | 5 +++- .../BaseDashboardControllerTest.java | 23 +++++++++++++------ 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java index 89d04a99de..187d8deabd 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java @@ -355,8 +355,6 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS return EdgeEventActionType.ASSIGNED_TO_EDGE; case UNASSIGNED_FROM_EDGE: return EdgeEventActionType.UNASSIGNED_FROM_EDGE; - case CREDENTIALS_UPDATED: - return EdgeEventActionType.CREDENTIALS_UPDATED; default: return null; } diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java index 197d1dceeb..ff09361a31 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java @@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; @@ -257,9 +258,11 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { } private void testSendNotificationMsgToEdgeServiceTime(EntityId entityId, TenantId tenantId, ActionType actionType, int cntTime) { + EdgeEventActionType edgeEventActionType = ActionType.CREDENTIALS_UPDATED.equals(actionType) ? + EdgeEventActionType.CREDENTIALS_UPDATED : edgeTypeByActionType(actionType); Mockito.verify(tbClusterService, times(cntTime)).sendNotificationMsgToEdge(Mockito.eq(tenantId), Mockito.any(), Mockito.eq(entityId), Mockito.any(), Mockito.isNull(), - Mockito.eq(edgeTypeByActionType(actionType))); + Mockito.eq(edgeEventActionType)); } private void testSendNotificationMsgToEdgeServiceTimeEntityEqAny(TenantId tenantId, ActionType actionType, int cntTime) { diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java index 78466427c8..3acb6c7d55 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java @@ -116,7 +116,9 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest Mockito.reset(tbClusterService, auditLogService); - doPost("/api/dashboard", dashboard).andExpect(statusReason(containsString(msgError))); + doPost("/api/dashboard", dashboard) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); dashboard.setTenantId(savedTenant.getId()); testNotifyEntityEqualsOneTimeError(dashboard, savedTenant.getId(), @@ -165,9 +167,11 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest savedDashboard.getTenantId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.DELETED, savedDashboard.getId().getId().toString()); - + String dashboardIdStr = savedDashboard.getId().getId().toString(); + String msgError = "Dashboard with id [" + dashboardIdStr + "] is not found"; doGet("/api/dashboard/" + savedDashboard.getId().getId().toString()) - .andExpect(status().isNotFound()); + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgError))); } @Test @@ -231,9 +235,12 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest dashboard.setTitle("My dashboard"); Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class); - doPost("/api/customer/" + Uuids.timeBased().toString() + String customerIdStr = Uuids.timeBased().toString(); + String msgError = "Customer with id [" + customerIdStr + "] is not found"; + doPost("/api/customer/" + customerIdStr + "/dashboard/" + savedDashboard.getId().getId().toString()) - .andExpect(status().isNotFound()); + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgError))); Mockito.reset(tbClusterService, auditLogService); testNotifyEntityNever(savedDashboard.getId(), savedDashboard); @@ -269,13 +276,15 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest doPost("/api/customer/" + savedCustomer.getId().getId().toString() + "/dashboard/" + savedDashboard.getId().getId().toString()) - .andExpect(status().isForbidden()); + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); Mockito.reset(tbClusterService, auditLogService); testNotifyEntityNever(savedDashboard.getId(), savedDashboard); doDelete("/api/tenant/" + savedTenant2.getId().getId().toString()) - .andExpect(status().isForbidden()); + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); testNotifyEntityNever(savedDashboard.getId(), savedDashboard); loginSysAdmin(); From ac3b133ec0dfdf9c08eac76bb685db4748d18ffb Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Wed, 29 Jun 2022 10:39:33 +0200 Subject: [PATCH 15/20] refactored queue validator --- .../server/dao/service/DataValidator.java | 24 +++++++++++++++++++ .../dao/service/validator/QueueValidator.java | 20 +++------------- .../validator/TenantProfileDataValidator.java | 18 +++----------- 3 files changed, 30 insertions(+), 32 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java index 1b27a2751f..81297b3594 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.service; import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; @@ -36,6 +37,11 @@ public abstract class DataValidator> { private static final Pattern EMAIL_PATTERN = Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}$", Pattern.CASE_INSENSITIVE); + private static final Pattern QUEUE_PATTERN = Pattern.compile("^[a-zA-Z0-9_.\\-]+$"); + + private static final String NAME = "name"; + private static final String TOPIC = "topic"; + // Returns old instance of the same object that is fetched during validation. public D validate(D data, Function tenantIdFunction) { try { @@ -134,4 +140,22 @@ public abstract class DataValidator> { } } + protected static void validateQueueName(String name) { + validateQueueNameOrTopic(name, NAME); + } + + protected static void validateQueueTopic(String topic) { + validateQueueNameOrTopic(topic, TOPIC); + } + + private static void validateQueueNameOrTopic(String value, String fieldName) { + if (StringUtils.isEmpty(value)) { + throw new DataValidationException(String.format("Queue %s should be specified!", fieldName)); + } + if (!QUEUE_PATTERN.matcher(value).matches()) { + throw new DataValidationException( + String.format("Queue %s contains a character other than ASCII alphanumerics, '.', '_' and '-'!", fieldName)); + } + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueValidator.java index a1d0df43d5..1eaa64f446 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueValidator.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.dao.service.validator; -import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.TenantProfile; @@ -29,8 +28,6 @@ import org.thingsboard.server.dao.queue.QueueDao; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; -import java.util.regex.Pattern; - @Component public class QueueValidator extends DataValidator { @@ -40,8 +37,6 @@ public class QueueValidator extends DataValidator { @Autowired private TbTenantProfileCache tenantProfileCache; - private final Pattern queueTopicPattern = Pattern.compile("^[a-zA-Z0-9_.\\-]+$"); - @Override protected void validateCreate(TenantId tenantId, Queue queue) { if (queueDao.findQueueByTenantIdAndName(tenantId, queue.getName()) != null) { @@ -77,18 +72,9 @@ public class QueueValidator extends DataValidator { } } - if (StringUtils.isEmpty(queue.getName())) { - throw new DataValidationException("Queue name should be specified!"); - } - if (!queueTopicPattern.matcher(queue.getName()).matches()) { - throw new DataValidationException("Queue name contains a character other than ASCII alphanumerics, '.', '_' and '-'!"); - } - if (StringUtils.isEmpty(queue.getTopic())) { - throw new DataValidationException("Queue topic should be specified!"); - } - if (!queueTopicPattern.matcher(queue.getTopic()).matches()) { - throw new DataValidationException("Queue topic contains a character other than ASCII alphanumerics, '.', '_' and '-'!"); - } + validateQueueName(queue.getName()); + validateQueueTopic(queue.getTopic()); + if (queue.getPollInterval() < 1) { throw new DataValidationException("Queue poll interval should be more then 0!"); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java index bfca262abf..feab5078e6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java @@ -34,7 +34,6 @@ import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; -import java.util.regex.Pattern; @Component public class TenantProfileDataValidator extends DataValidator { @@ -46,8 +45,6 @@ public class TenantProfileDataValidator extends DataValidator { @Lazy private TenantProfileService tenantProfileService; - private final Pattern queueTopicPattern = Pattern.compile("^[a-zA-Z0-9_.\\-]+$"); - @Override protected void validateDataImpl(TenantId tenantId, TenantProfile tenantProfile) { if (StringUtils.isEmpty(tenantProfile.getName())) { @@ -110,18 +107,9 @@ public class TenantProfileDataValidator extends DataValidator { } private void validateQueueConfiguration(TenantProfileQueueConfiguration queue) { - if (StringUtils.isEmpty(queue.getName())) { - throw new DataValidationException("Queue name should be specified!"); - } - if (!queueTopicPattern.matcher(queue.getName()).matches()) { - throw new DataValidationException("Queue name contains a character other than ASCII alphanumerics, '.', '_' and '-'!"); - } - if (StringUtils.isEmpty(queue.getTopic())) { - throw new DataValidationException("Queue topic should be specified!"); - } - if (!queueTopicPattern.matcher(queue.getTopic()).matches()) { - throw new DataValidationException("Queue topic contains a character other than ASCII alphanumerics, '.', '_' and '-'!"); - } + validateQueueName(queue.getName()); + validateQueueTopic(queue.getTopic()); + if (queue.getPollInterval() < 1) { throw new DataValidationException("Queue poll interval should be more then 0!"); } From 27e3070b7a8cbb04ad8b0e7a3133410d914bcfb0 Mon Sep 17 00:00:00 2001 From: fe-dev Date: Wed, 29 Jun 2022 15:02:30 +0300 Subject: [PATCH 16/20] UI: Refactoring --- .../app/modules/home/components/queue/queue-form.component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts index c1aecea4b2..add3c26007 100644 --- a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts +++ b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts @@ -205,11 +205,12 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, OnDestr if (type === QueueSubmitStrategyTypes.BATCH) { batchSizeField.patchValue(1000, {emitEvent: false}); batchSizeField.setValidators([Validators.min(1), Validators.required]); + batchSizeField.updateValueAndValidity({emitEvent: false}); this.hideBatchSize = true; } else { batchSizeField.patchValue(null, {emitEvent: false}); batchSizeField.clearValidators(); - batchSizeField.updateValueAndValidity(); + batchSizeField.updateValueAndValidity({emitEvent: false}); this.hideBatchSize = false; } } From fb0a38fe16c8d38ce8d73fe27302409965a55dc0 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 29 Jun 2022 15:02:43 +0300 Subject: [PATCH 17/20] refactoring: tests magError from function --- .../controller/AbstractNotifyEntityTest.java | 10 +++++++++ .../controller/BaseAlarmControllerTest.java | 4 ++-- .../controller/BaseAssetControllerTest.java | 21 ++++++++----------- .../BaseCustomerControllerTest.java | 17 +++++++-------- .../BaseDashboardControllerTest.java | 10 ++++----- 5 files changed, 33 insertions(+), 29 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java index ff09361a31..617e0637e8 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java @@ -59,6 +59,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { protected GatewayNotificationsService gatewayNotificationsService; protected final String msgErrorPermission = "You don't have permission to perform this operation!"; + protected final String msgErrorShouldBeSpecified = "should be specified"; + protected void testNotifyEntityAllOneTime(HasName entity, EntityId entityId, EntityId originatorId, TenantId tenantId, CustomerId customerId, UserId userId, String userName, @@ -490,4 +492,12 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { .substring(entity.getClass().toString().lastIndexOf(".") + 1).toUpperCase(Locale.ENGLISH)), ModelConstants.NULL_UUID); } + + protected String msgErrorFieldLength(String fieldName){ + return "length of " + fieldName + " must be equal or less than 255"; + } + + protected String msgErrorNoFound(String entityClassName, String assetIdStr){ + return entityClassName + " with id [" + assetIdStr + "] is not found"; + } } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java index 9eab1e56b3..14e260ab14 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java @@ -322,8 +322,8 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - doPost("/api/alarm/" + alarm.getId() + "/ack").andExpect(status() - .isForbidden()) + doPost("/api/alarm/" + alarm.getId() + "/ack") + .andExpect(status().isForbidden()) .andExpect(statusReason(containsString(msgErrorPermission))); } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java index b784b3a596..44697c40fb 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java @@ -122,7 +122,7 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - String msgError = "length of name must be equal or less than 255"; + String msgError = msgErrorFieldLength("name"); doPost("/api/asset", asset) .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString(msgError))); @@ -133,7 +133,7 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { asset.setName("Normal name"); asset.setType(RandomStringUtils.randomAlphabetic(300)); - msgError = "length of type must be equal or less than 255"; + msgError = msgErrorFieldLength("type"); doPost("/api/asset", asset) .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString(msgError))); @@ -144,7 +144,7 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { asset.setType("default"); asset.setLabel(RandomStringUtils.randomAlphabetic(300)); - msgError = "length of label must be equal or less than 255"; + msgError = msgErrorFieldLength("label"); doPost("/api/asset", asset) .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString(msgError))); @@ -245,10 +245,9 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { ActionType.DELETED, savedAsset.getId().getId().toString()); String assetIdStr = savedAsset.getId().getId().toString(); - String msgError = "Asset with id [" + assetIdStr + "] is not found"; - doGet("/api/asset/" + savedAsset.getId().getId().toString()) + doGet("/api/asset/" + assetIdStr) .andExpect(status().isNotFound()) - .andExpect(statusReason(containsString(msgError))); + .andExpect(statusReason(containsString(msgErrorNoFound("Asset", assetIdStr)))); } @Test @@ -288,10 +287,9 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { .andExpect(status().isOk()); String assetIdStr = savedAsset1.getId().getId().toString(); - msgError = "Asset with id [" + assetIdStr + "] is not found"; doGet("/api/asset/" + assetIdStr) .andExpect(status().isNotFound()) - .andExpect(statusReason(containsString(msgError))); + .andExpect(statusReason(containsString(msgErrorNoFound("Asset", assetIdStr)))); } @Test @@ -301,7 +299,7 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - String msgError = "Asset type should be specified"; + String msgError = "Asset type " + msgErrorShouldBeSpecified; doPost("/api/asset", asset) .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString(msgError))); @@ -317,7 +315,7 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - String msgError = "Asset name should be specified"; + String msgError = "Asset name " + msgErrorShouldBeSpecified; doPost("/api/asset", asset) .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString(msgError))); @@ -374,11 +372,10 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); String customerIdStr = Uuids.timeBased().toString(); - String msgError = "Customer with id [" + customerIdStr + "] is not found"; doPost("/api/customer/" + customerIdStr + "/asset/" + savedAsset.getId().getId().toString()) .andExpect(status().isNotFound()) - .andExpect(statusReason(containsString(msgError))); + .andExpect(statusReason(containsString(msgErrorNoFound("Customer", customerIdStr)))); testNotifyEntityNever(asset.getId(), asset); } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java index 1b1d269c79..817e8d4ca9 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java @@ -126,7 +126,7 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest Mockito.reset(tbClusterService, auditLogService); - String msgError = "length of title must be equal or less than 255"; + String msgError = msgErrorFieldLength("title"); doPost("/api/customer", customer) .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString(msgError))); @@ -138,7 +138,7 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest customer.setTitle("Normal title"); customer.setCity(RandomStringUtils.randomAlphabetic(300)); - msgError = "length of city must be equal or less than 255"; + msgError = msgErrorFieldLength("city"); doPost("/api/customer", customer) .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString(msgError))); @@ -149,7 +149,7 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest customer.setCity("Normal city"); customer.setCountry(RandomStringUtils.randomAlphabetic(300)); - msgError = "length of country must be equal or less than 255"; + msgError = msgErrorFieldLength("country"); doPost("/api/customer", customer) .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString(msgError))); @@ -160,7 +160,7 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest customer.setCountry("Ukraine"); customer.setPhone(RandomStringUtils.randomAlphabetic(300)); - msgError = "length of phone must be equal or less than 255"; + msgError = msgErrorFieldLength("phone"); doPost("/api/customer", customer) .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString(msgError))); @@ -171,7 +171,7 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest customer.setPhone("+3892555554512"); customer.setState(RandomStringUtils.randomAlphabetic(300)); - msgError = "length of state must be equal or less than 255"; + msgError = msgErrorFieldLength("state"); doPost("/api/customer", customer) .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString(msgError))); @@ -182,7 +182,7 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest customer.setState("Normal state"); customer.setZip(RandomStringUtils.randomAlphabetic(300)); - msgError = "length of zip or postal code must be equal or less than 255"; + msgError = msgErrorFieldLength("zip or postal code"); doPost("/api/customer", customer) .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString(msgError))); @@ -255,16 +255,15 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest tenantAdmin.getEmail(), ActionType.DELETED, savedCustomer.getId().getId().toString()); String customerIdStr = savedCustomer.getId().getId().toString(); - String msgError = "Customer with id [" + customerIdStr + "] is not found"; doGet("/api/customer/" + customerIdStr) .andExpect(status().isNotFound()) - .andExpect(statusReason(containsString(msgError))); + .andExpect(statusReason(containsString(msgErrorNoFound("Customer", customerIdStr)))); } @Test public void testSaveCustomerWithEmptyTitle() throws Exception { Customer customer = new Customer(); - String msgError = "Customer title should be specified"; + String msgError = "Customer title " + msgErrorShouldBeSpecified; Mockito.reset(tbClusterService, auditLogService); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java index 3acb6c7d55..5a9a3d5578 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java @@ -112,7 +112,7 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest public void testSaveDashboardInfoWithViolationOfValidation() throws Exception { Dashboard dashboard = new Dashboard(); dashboard.setTitle(RandomStringUtils.randomAlphabetic(300)); - String msgError = "length of title must be equal or less than 255"; + String msgError = msgErrorFieldLength("title"); Mockito.reset(tbClusterService, auditLogService); @@ -168,16 +168,15 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest savedDashboard.getId().getId().toString()); String dashboardIdStr = savedDashboard.getId().getId().toString(); - String msgError = "Dashboard with id [" + dashboardIdStr + "] is not found"; doGet("/api/dashboard/" + savedDashboard.getId().getId().toString()) .andExpect(status().isNotFound()) - .andExpect(statusReason(containsString(msgError))); + .andExpect(statusReason(containsString(msgErrorNoFound("Dashboard", dashboardIdStr)))); } @Test public void testSaveDashboardWithEmptyTitle() throws Exception { Dashboard dashboard = new Dashboard(); - String msgError = "Dashboard title should be specified"; + String msgError = "Dashboard title " + msgErrorShouldBeSpecified;; Mockito.reset(tbClusterService, auditLogService); @@ -236,11 +235,10 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class); String customerIdStr = Uuids.timeBased().toString(); - String msgError = "Customer with id [" + customerIdStr + "] is not found"; doPost("/api/customer/" + customerIdStr + "/dashboard/" + savedDashboard.getId().getId().toString()) .andExpect(status().isNotFound()) - .andExpect(statusReason(containsString(msgError))); + .andExpect(statusReason(containsString(msgErrorNoFound("Customer", customerIdStr)))); Mockito.reset(tbClusterService, auditLogService); testNotifyEntityNever(savedDashboard.getId(), savedDashboard); From af69872a1df938e1f6ceddf668bfb94540f96064 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 29 Jun 2022 17:11:41 +0300 Subject: [PATCH 18/20] refactoring: tests Device add .andExpect... --- .../controller/BaseAssetControllerTest.java | 1 + .../controller/BaseDeviceControllerTest.java | 63 +++++++++++++------ 2 files changed, 44 insertions(+), 20 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java index 44697c40fb..452d4f40f0 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java @@ -168,6 +168,7 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { testNotifyEntityNever(savedAsset.getId(), savedAsset); + //TODO fix bug: "No value at JSON path "$.message" doDelete("/api/asset" + savedAsset.getId().getId().toString()) .andExpect(status().isNotFound()); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java index b6dd8b267f..96f1924d11 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java @@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.edge.Edge; 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.id.EntityId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.relation.EntityRelation; @@ -157,7 +158,9 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); String msgError = "length of name must be equal or less than 255"; - doPost("/api/device", device).andExpect(statusReason(containsString(msgError))); + doPost("/api/device", device) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); testNotifyEntityEqualsOneTimeError(device, savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); @@ -167,7 +170,9 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { device.setTenantId(savedTenant.getId()); msgError = "length of type must be equal or less than 255"; device.setType(RandomStringUtils.randomAlphabetic(300)); - doPost("/api/device", device).andExpect(statusReason(containsString(msgError))); + doPost("/api/device", device) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); testNotifyEntityEqualsOneTimeError(device, savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); @@ -177,7 +182,9 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { msgError = "length of label must be equal or less than 255"; device.setType("Normal type"); device.setLabel(RandomStringUtils.randomAlphabetic(300)); - doPost("/api/device", device).andExpect(statusReason(containsString(msgError))); + doPost("/api/device", device) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); testNotifyEntityEqualsOneTimeError(device, savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); @@ -201,8 +208,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); - doDelete("/api/device/" + savedDevice.getId().getId().toString()) - .andExpect(status().isNotFound()); + String savedDeviceIdStr = savedDevice.getId().getId().toString(); + doDelete("/api/device/" + savedDeviceIdStr) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Device", savedDeviceIdStr)))); testNotifyEntityNever(savedDevice.getId(), savedDevice); testNotificationUpdateGatewayNever(); @@ -283,8 +292,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.DELETED, savedDevice.getId().getId().toString()); testNotificationDeleteGatewayOneTime(savedDevice); - doGet("/api/device/" + savedDevice.getId().getId()) - .andExpect(status().isNotFound()); + EntityId savedDeviceId = savedDevice.getId(); + doGet("/api/device/" + savedDeviceId) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Device", savedDeviceId.getId().toString())))); } @Test @@ -310,7 +321,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); - String msgError = "Device name should be specified"; + String msgError = "Device name " + msgErrorShouldBeSpecified; doPost("/api/device", device) .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString(msgError))); @@ -371,9 +382,11 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); - doPost("/api/customer/" + Uuids.timeBased().toString() - + "/device/" + savedDevice.getId().getId()) - .andExpect(status().isNotFound()); + String customerIdStr = savedDevice.getId().toString(); + doPost("/api/customer/" + customerIdStr + + "/device/" + savedDevice.getId().getId()) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Customer", customerIdStr)))); testNotifyEntityNever(savedDevice.getId(), savedDevice); testNotificationUpdateGatewayNever(); @@ -412,7 +425,8 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { doPost("/api/customer/" + savedCustomer.getId().getId() + "/device/" + savedDevice.getId().getId()) - .andExpect(status().isForbidden()); + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); testNotifyEntityNever(savedDevice.getId(), savedDevice); testNotificationUpdateGatewayNever(); @@ -468,7 +482,8 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); doPost("/api/device/credentials", deviceCredentials) - .andExpect(status().isBadRequest()); + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("Incorrect deviceId null"))); testNotifyEntityNever(deviceCredentials.getDeviceId(), new Device()); testNotificationUpdateGatewayNever(); @@ -486,7 +501,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); - String msgError = "Device credentials type should be specified"; + String msgError = "Device credentials type " + msgErrorShouldBeSpecified; doPost("/api/device/credentials", deviceCredentials) .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString(msgError))); @@ -509,7 +524,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); - String msgError = "Device credentials id should be specified"; + String msgError = "Device credentials id " + msgErrorShouldBeSpecified; doPost("/api/device/credentials", deviceCredentials) .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString(msgError))); @@ -553,14 +568,16 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { device.setName("My device"); device.setType("default"); Device savedDevice = doPost("/api/device", device, Device.class); + DeviceId deviceTimeBasedId = new DeviceId(Uuids.timeBased()); DeviceCredentials deviceCredentials = doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); - deviceCredentials.setDeviceId(new DeviceId(Uuids.timeBased())); + deviceCredentials.setDeviceId(deviceTimeBasedId); Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); doPost("/api/device/credentials", deviceCredentials) - .andExpect(status().isNotFound()); + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Device", deviceTimeBasedId.toString())))); testNotifyEntityNever(savedDevice.getId(), savedDevice); testNotificationUpdateGatewayNever(); @@ -1029,9 +1046,12 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); - Device assignedDevice = doPost("/api/tenant/" + savedDifferentTenant.getId().getId() + "/device/" + savedDevice.getId().getId(), Device.class); + Device assignedDevice = doPost("/api/tenant/" + savedDifferentTenant.getId().getId() + "/device/" + + savedDevice.getId().getId(), Device.class); - doGet("/api/device/" + assignedDevice.getId().getId(), Device.class, status().isNotFound()); + doGet("/api/device/" + assignedDevice.getId().getId()) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Device", assignedDevice.getId().getId().toString())))); testNotifyEntityOneTimeMsgToEdgeServiceNever(assignedDevice, assignedDevice.getId(), assignedDevice.getId(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), @@ -1043,7 +1063,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Device foundDevice1 = doGet("/api/device/" + assignedDevice.getId().getId(), 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()); + doGet("/api/relation?fromId=" + savedDevice.getId().getId() + "&fromType=DEVICE&relationType=Contains&toId=" + + savedAnotherDevice.getId().getId() + "&toType=DEVICE") + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Device", savedAnotherDevice.getId().getId().toString())))); loginSysAdmin(); doDelete("/api/tenant/" + savedDifferentTenant.getId().getId()) From 4b5e94e3cc6e9813bed3385e9d4bb2c766ff13a4 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 29 Jun 2022 17:35:13 +0300 Subject: [PATCH 19/20] Add Version control REST API methods to Rest Client. Remove redudant branch request parameter from some version control REST API methods. --- .../server/controller/AssetController.java | 4 +- .../server/controller/DeviceController.java | 4 +- .../server/controller/EdgeController.java | 4 +- .../EntitiesVersionControlController.java | 22 +- .../service/asset/AssetBulkImportService.java | 2 +- .../device/DeviceBulkImportService.java | 2 +- .../service/edge/EdgeBulkImportService.java | 2 +- .../csv/AbstractBulkImportService.java | 3 + .../DefaultEntitiesVersionControlService.java | 10 +- .../DefaultGitVersionControlQueueService.java | 119 ++++---- .../vc/EntitiesVersionControlService.java | 6 +- .../vc/GitVersionControlQueueService.java | 4 +- .../importing/csv/BulkImportColumnType.java | 2 +- .../ie/importing/csv/BulkImportRequest.java | 2 +- .../ie/importing/csv/BulkImportResult.java | 2 +- .../common/data/sync/vc/BranchInfo.java | 3 + .../vc/request/load/VersionLoadRequest.java | 1 - .../server/service/sync/vc/GitRepository.java | 6 +- .../thingsboard/rest/client/RestClient.java | 266 +++++++++++++++++- .../http/entities-version-control.service.ts | 6 +- .../vc/complex-version-load.component.ts | 4 - .../vc/entity-version-diff.component.ts | 6 +- .../vc/entity-version-restore.component.ts | 4 - .../vc/entity-versions-table.component.ts | 3 - ui-ngx/src/app/shared/models/vc.models.ts | 1 - 25 files changed, 369 insertions(+), 119 deletions(-) rename {application/src/main/java/org/thingsboard/server/service => common/data/src/main/java/org/thingsboard/server/common/data}/sync/ie/importing/csv/BulkImportColumnType.java (97%) rename {application/src/main/java/org/thingsboard/server/service => common/data/src/main/java/org/thingsboard/server/common/data}/sync/ie/importing/csv/BulkImportRequest.java (94%) rename {application/src/main/java/org/thingsboard/server/service => common/data/src/main/java/org/thingsboard/server/common/data}/sync/ie/importing/csv/BulkImportResult.java (94%) diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java index 13ef646eaa..f1ff6aa3c6 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java @@ -51,8 +51,8 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.asset.AssetBulkImportService; -import org.thingsboard.server.service.sync.ie.importing.csv.BulkImportRequest; -import org.thingsboard.server.service.sync.ie.importing.csv.BulkImportResult; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportRequest; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportResult; import org.thingsboard.server.service.entitiy.asset.TbAssetService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index 183b5ea048..895e711073 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -66,8 +66,8 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.device.DeviceBulkImportService; -import org.thingsboard.server.service.sync.ie.importing.csv.BulkImportRequest; -import org.thingsboard.server.service.sync.ie.importing.csv.BulkImportResult; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportRequest; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportResult; import org.thingsboard.server.service.entitiy.device.TbDeviceService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java index b3c1792b6a..5b2c6a6a9d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java @@ -53,8 +53,8 @@ import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.edge.EdgeBulkImportService; import org.thingsboard.server.service.entitiy.edge.TbEdgeService; -import org.thingsboard.server.service.sync.ie.importing.csv.BulkImportRequest; -import org.thingsboard.server.service.sync.ie.importing.csv.BulkImportResult; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportRequest; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportResult; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java index 2e37bcbe59..65b5f10407 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java @@ -310,15 +310,13 @@ public class EntitiesVersionControlController extends BaseController { "Each entity item in the result has `externalId` property. " + "Entities order will be the same as in the repository." + TENANT_AUTHORITY_PARAGRAPH) - @GetMapping(value = "/entity/{entityType}/{versionId}", params = {"branch"}) + @GetMapping(value = "/entity/{entityType}/{versionId}") public DeferredResult> listEntitiesAtVersion(@ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @PathVariable EntityType entityType, @ApiParam(value = VERSION_ID_PARAM_DESCRIPTION, required = true) - @PathVariable String versionId, - @ApiParam(value = BRANCH_PARAM_DESCRIPTION, required = true) - @RequestParam String branch) throws Exception { + @PathVariable String versionId) throws Exception { accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); - return wrapFuture(versionControlService.listEntitiesAtVersion(getTenantId(), branch, versionId, entityType)); + return wrapFuture(versionControlService.listEntitiesAtVersion(getTenantId(), versionId, entityType)); } @ApiOperation(value = "List all entities at version (listAllEntitiesAtVersion)", notes = "" + @@ -326,13 +324,11 @@ public class EntitiesVersionControlController extends BaseController { "Response type is the same as for listAllEntitiesAtVersion API method. \n" + "Returned entities order will be the same as in the repository." + TENANT_AUTHORITY_PARAGRAPH) - @GetMapping(value = "/entity/{versionId}", params = {"branch"}) + @GetMapping(value = "/entity/{versionId}") public DeferredResult> listAllEntitiesAtVersion(@ApiParam(value = VERSION_ID_PARAM_DESCRIPTION, required = true) - @PathVariable String versionId, - @ApiParam(value = BRANCH_PARAM_DESCRIPTION, required = true) - @RequestParam String branch) throws Exception { + @PathVariable String versionId) throws Exception { accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); - return wrapFuture(versionControlService.listAllEntitiesAtVersion(getTenantId(), branch, versionId)); + return wrapFuture(versionControlService.listAllEntitiesAtVersion(getTenantId(), versionId)); } @ApiOperation(value = "Get entity data info (getEntityDataInfo)", notes = "" + @@ -357,18 +353,16 @@ public class EntitiesVersionControlController extends BaseController { "Returns an object with current entity data and the one at a specific version. " + "Entity data structure is the same as stored in a repository. " + TENANT_AUTHORITY_PARAGRAPH) - @GetMapping(value = "/diff/{entityType}/{internalEntityUuid}", params = {"branch", "versionId"}) + @GetMapping(value = "/diff/{entityType}/{internalEntityUuid}", params = {"versionId"}) public DeferredResult compareEntityDataToVersion(@ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @PathVariable EntityType entityType, @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable UUID internalEntityUuid, - @ApiParam(value = BRANCH_PARAM_DESCRIPTION) - @RequestParam String branch, @ApiParam(value = VERSION_ID_PARAM_DESCRIPTION, required = true) @RequestParam String versionId) throws Exception { accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, internalEntityUuid); - return wrapFuture(versionControlService.compareEntityDataToVersion(getCurrentUser(), branch, entityId, versionId)); + return wrapFuture(versionControlService.compareEntityDataToVersion(getCurrentUser(), entityId, versionId)); } @ApiOperation(value = "Load entities version (loadEntitiesVersion)", notes = "" + diff --git a/application/src/main/java/org/thingsboard/server/service/asset/AssetBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/asset/AssetBulkImportService.java index 5eaeec0b73..ff89c3dd06 100644 --- a/application/src/main/java/org/thingsboard/server/service/asset/AssetBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/asset/AssetBulkImportService.java @@ -27,7 +27,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.sync.ie.importing.csv.AbstractBulkImportService; -import org.thingsboard.server.service.sync.ie.importing.csv.BulkImportColumnType; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportColumnType; import org.thingsboard.server.service.entitiy.asset.TbAssetService; import org.thingsboard.server.service.security.model.SecurityUser; diff --git a/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java index c04dd5ce15..8b2126905f 100644 --- a/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java @@ -50,7 +50,7 @@ import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.sync.ie.importing.csv.AbstractBulkImportService; -import org.thingsboard.server.service.sync.ie.importing.csv.BulkImportColumnType; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportColumnType; import org.thingsboard.server.service.entitiy.device.TbDeviceService; import org.thingsboard.server.service.security.model.SecurityUser; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java index 404e5af5fd..8372e2ba0e 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java @@ -29,7 +29,7 @@ import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.sync.ie.importing.csv.AbstractBulkImportService; -import org.thingsboard.server.service.sync.ie.importing.csv.BulkImportColumnType; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportColumnType; import org.thingsboard.server.service.entitiy.edge.TbEdgeService; import org.thingsboard.server.service.security.model.SecurityUser; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java index b92e100401..becf92771f 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java @@ -39,6 +39,9 @@ import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.DataType; import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportColumnType; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportRequest; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportResult; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.controller.BaseController; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java index 88a15f7cdc..00e98cd3a1 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java @@ -241,13 +241,13 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont } @Override - public ListenableFuture> listEntitiesAtVersion(TenantId tenantId, String branch, String versionId, EntityType entityType) throws Exception { - return gitServiceQueue.listEntitiesAtVersion(tenantId, branch, versionId, entityType); + public ListenableFuture> listEntitiesAtVersion(TenantId tenantId, String versionId, EntityType entityType) throws Exception { + return gitServiceQueue.listEntitiesAtVersion(tenantId, versionId, entityType); } @Override - public ListenableFuture> listAllEntitiesAtVersion(TenantId tenantId, String branch, String versionId) throws Exception { - return gitServiceQueue.listEntitiesAtVersion(tenantId, branch, versionId); + public ListenableFuture> listAllEntitiesAtVersion(TenantId tenantId, String versionId) throws Exception { + return gitServiceQueue.listEntitiesAtVersion(tenantId, versionId); } @SuppressWarnings({"UnstableApiUsage", "rawtypes"}) @@ -451,7 +451,7 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont } @Override - public ListenableFuture compareEntityDataToVersion(User user, String branch, EntityId entityId, String versionId) throws Exception { + public ListenableFuture compareEntityDataToVersion(User user, EntityId entityId, String versionId) throws Exception { HasId entity = exportableEntitiesService.findEntityByTenantIdAndId(user.getTenantId(), entityId); if (!(entity instanceof ExportableEntity)) throw new IllegalArgumentException("Unsupported entity type"); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitVersionControlQueueService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitVersionControlQueueService.java index 6eaa2df74f..c9b620ad1c 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitVersionControlQueueService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitVersionControlQueueService.java @@ -245,7 +245,7 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu } @Override - public ListenableFuture> listEntitiesAtVersion(TenantId tenantId, String branch, String versionId, EntityType entityType) { + public ListenableFuture> listEntitiesAtVersion(TenantId tenantId, String versionId, EntityType entityType) { return listEntitiesAtVersion(tenantId, ListEntitiesRequestMsg.newBuilder() .setVersionId(versionId) .setEntityType(entityType.name()) @@ -253,7 +253,7 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu } @Override - public ListenableFuture> listEntitiesAtVersion(TenantId tenantId, String branch, String versionId) { + public ListenableFuture> listEntitiesAtVersion(TenantId tenantId, String versionId) { return listEntitiesAtVersion(tenantId, ListEntitiesRequestMsg.newBuilder() .setVersionId(versionId) .build()); @@ -385,66 +385,71 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu if (!StringUtils.isEmpty(vcResponseMsg.getError())) { future.setException(new RuntimeException(vcResponseMsg.getError())); } else { - if (vcResponseMsg.hasGenericResponse()) { - future.set(null); - } else if (vcResponseMsg.hasCommitResponse()) { - var commitResponse = vcResponseMsg.getCommitResponse(); - var commitResult = new VersionCreationResult(); - if (commitResponse.getTs() > 0) { - commitResult.setVersion(new EntityVersion(commitResponse.getTs(), commitResponse.getCommitId(), commitResponse.getName(), commitResponse.getAuthor())); - } - commitResult.setAdded(commitResponse.getAdded()); - commitResult.setRemoved(commitResponse.getRemoved()); - commitResult.setModified(commitResponse.getModified()); - commitResult.setDone(true); - ((CommitGitRequest) request).getFuture().set(commitResult); - } else if (vcResponseMsg.hasListBranchesResponse()) { - var listBranchesResponse = vcResponseMsg.getListBranchesResponse(); - ((ListBranchesGitRequest) request).getFuture().set(listBranchesResponse.getBranchesList().stream().map(this::getBranchInfo).collect(Collectors.toList())); - } else if (vcResponseMsg.hasListEntitiesResponse()) { - var listEntitiesResponse = vcResponseMsg.getListEntitiesResponse(); - ((ListEntitiesGitRequest) request).getFuture().set( - listEntitiesResponse.getEntitiesList().stream().map(this::getVersionedEntityInfo).collect(Collectors.toList())); - } else if (vcResponseMsg.hasListVersionsResponse()) { - var listVersionsResponse = vcResponseMsg.getListVersionsResponse(); - ((ListVersionsGitRequest) request).getFuture().set(toPageData(listVersionsResponse)); - } else if (vcResponseMsg.hasEntityContentResponse()) { - TransportProtos.EntityContentResponseMsg responseMsg = vcResponseMsg.getEntityContentResponse(); - log.trace("[{}] received chunk {} for 'getEntity'", responseMsg.getChunkedMsgId(), responseMsg.getChunkIndex()); - var joined = joinChunks(requestId, responseMsg, 1); - if (joined.isPresent()) { - log.trace("[{}] collected all chunks for 'getEntity'", responseMsg.getChunkedMsgId()); - ((EntityContentGitRequest) request).getFuture().set(joined.get().get(0)); - } else { - completed = false; - } - } else if (vcResponseMsg.hasEntitiesContentResponse()) { - TransportProtos.EntitiesContentResponseMsg responseMsg = vcResponseMsg.getEntitiesContentResponse(); - TransportProtos.EntityContentResponseMsg item = responseMsg.getItem(); - if (responseMsg.getItemsCount() > 0) { - var joined = joinChunks(requestId, item, responseMsg.getItemsCount()); + try { + if (vcResponseMsg.hasGenericResponse()) { + future.set(null); + } else if (vcResponseMsg.hasCommitResponse()) { + var commitResponse = vcResponseMsg.getCommitResponse(); + var commitResult = new VersionCreationResult(); + if (commitResponse.getTs() > 0) { + commitResult.setVersion(new EntityVersion(commitResponse.getTs(), commitResponse.getCommitId(), commitResponse.getName(), commitResponse.getAuthor())); + } + commitResult.setAdded(commitResponse.getAdded()); + commitResult.setRemoved(commitResponse.getRemoved()); + commitResult.setModified(commitResponse.getModified()); + commitResult.setDone(true); + ((CommitGitRequest) request).getFuture().set(commitResult); + } else if (vcResponseMsg.hasListBranchesResponse()) { + var listBranchesResponse = vcResponseMsg.getListBranchesResponse(); + ((ListBranchesGitRequest) request).getFuture().set(listBranchesResponse.getBranchesList().stream().map(this::getBranchInfo).collect(Collectors.toList())); + } else if (vcResponseMsg.hasListEntitiesResponse()) { + var listEntitiesResponse = vcResponseMsg.getListEntitiesResponse(); + ((ListEntitiesGitRequest) request).getFuture().set( + listEntitiesResponse.getEntitiesList().stream().map(this::getVersionedEntityInfo).collect(Collectors.toList())); + } else if (vcResponseMsg.hasListVersionsResponse()) { + var listVersionsResponse = vcResponseMsg.getListVersionsResponse(); + ((ListVersionsGitRequest) request).getFuture().set(toPageData(listVersionsResponse)); + } else if (vcResponseMsg.hasEntityContentResponse()) { + TransportProtos.EntityContentResponseMsg responseMsg = vcResponseMsg.getEntityContentResponse(); + log.trace("[{}] received chunk {} for 'getEntity'", responseMsg.getChunkedMsgId(), responseMsg.getChunkIndex()); + var joined = joinChunks(requestId, responseMsg, 1); if (joined.isPresent()) { - ((EntitiesContentGitRequest) request).getFuture().set(joined.get()); + log.trace("[{}] collected all chunks for 'getEntity'", responseMsg.getChunkedMsgId()); + ((EntityContentGitRequest) request).getFuture().set(joined.get().get(0)); } else { completed = false; } - } else { - ((EntitiesContentGitRequest) request).getFuture().set(Collections.emptyList()); + } else if (vcResponseMsg.hasEntitiesContentResponse()) { + TransportProtos.EntitiesContentResponseMsg responseMsg = vcResponseMsg.getEntitiesContentResponse(); + TransportProtos.EntityContentResponseMsg item = responseMsg.getItem(); + if (responseMsg.getItemsCount() > 0) { + var joined = joinChunks(requestId, item, responseMsg.getItemsCount()); + if (joined.isPresent()) { + ((EntitiesContentGitRequest) request).getFuture().set(joined.get()); + } else { + completed = false; + } + } else { + ((EntitiesContentGitRequest) request).getFuture().set(Collections.emptyList()); + } + } else if (vcResponseMsg.hasVersionsDiffResponse()) { + TransportProtos.VersionsDiffResponseMsg diffResponse = vcResponseMsg.getVersionsDiffResponse(); + List entityVersionsDiffList = diffResponse.getDiffList().stream() + .map(diff -> EntityVersionsDiff.builder() + .externalId(EntityIdFactory.getByTypeAndUuid(EntityType.valueOf(diff.getEntityType()), + new UUID(diff.getEntityIdMSB(), diff.getEntityIdLSB()))) + .entityDataAtVersion1(StringUtils.isNotEmpty(diff.getEntityDataAtVersion1()) ? + toData(diff.getEntityDataAtVersion1()) : null) + .entityDataAtVersion2(StringUtils.isNotEmpty(diff.getEntityDataAtVersion2()) ? + toData(diff.getEntityDataAtVersion2()) : null) + .rawDiff(diff.getRawDiff()) + .build()) + .collect(Collectors.toList()); + ((VersionsDiffGitRequest) request).getFuture().set(entityVersionsDiffList); } - } else if (vcResponseMsg.hasVersionsDiffResponse()) { - TransportProtos.VersionsDiffResponseMsg diffResponse = vcResponseMsg.getVersionsDiffResponse(); - List entityVersionsDiffList = diffResponse.getDiffList().stream() - .map(diff -> EntityVersionsDiff.builder() - .externalId(EntityIdFactory.getByTypeAndUuid(EntityType.valueOf(diff.getEntityType()), - new UUID(diff.getEntityIdMSB(), diff.getEntityIdLSB()))) - .entityDataAtVersion1(StringUtils.isNotEmpty(diff.getEntityDataAtVersion1()) ? - toData(diff.getEntityDataAtVersion1()) : null) - .entityDataAtVersion2(StringUtils.isNotEmpty(diff.getEntityDataAtVersion2()) ? - toData(diff.getEntityDataAtVersion2()) : null) - .rawDiff(diff.getRawDiff()) - .build()) - .collect(Collectors.toList()); - ((VersionsDiffGitRequest) request).getFuture().set(entityVersionsDiffList); + } catch (Exception e) { + future.setException(e); + throw e; } } if (completed) { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java index 8bc2049e33..8a2410e932 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java @@ -49,15 +49,15 @@ public interface EntitiesVersionControlService { ListenableFuture> listVersions(TenantId tenantId, String branch, PageLink pageLink) throws Exception; - ListenableFuture> listEntitiesAtVersion(TenantId tenantId, String branch, String versionId, EntityType entityType) throws Exception; + ListenableFuture> listEntitiesAtVersion(TenantId tenantId, String versionId, EntityType entityType) throws Exception; - ListenableFuture> listAllEntitiesAtVersion(TenantId tenantId, String branch, String versionId) throws Exception; + ListenableFuture> listAllEntitiesAtVersion(TenantId tenantId, String versionId) throws Exception; UUID loadEntitiesVersion(User user, VersionLoadRequest request) throws Exception; VersionLoadResult getVersionLoadStatus(User user, UUID requestId) throws ThingsboardException; - ListenableFuture compareEntityDataToVersion(User user, String branch, EntityId entityId, String versionId) throws Exception; + ListenableFuture compareEntityDataToVersion(User user, EntityId entityId, String versionId) throws Exception; ListenableFuture> listBranches(TenantId tenantId) throws Exception; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/GitVersionControlQueueService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/GitVersionControlQueueService.java index cc83479896..feba7db76d 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/GitVersionControlQueueService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/GitVersionControlQueueService.java @@ -52,9 +52,9 @@ public interface GitVersionControlQueueService { ListenableFuture> listVersions(TenantId tenantId, String branch, EntityId entityId, PageLink pageLink); - ListenableFuture> listEntitiesAtVersion(TenantId tenantId, String branch, String versionId, EntityType entityType); + ListenableFuture> listEntitiesAtVersion(TenantId tenantId, String versionId, EntityType entityType); - ListenableFuture> listEntitiesAtVersion(TenantId tenantId, String branch, String versionId); + ListenableFuture> listEntitiesAtVersion(TenantId tenantId, String versionId); ListenableFuture> listBranches(TenantId tenantId); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/BulkImportColumnType.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/importing/csv/BulkImportColumnType.java similarity index 97% rename from application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/BulkImportColumnType.java rename to common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/importing/csv/BulkImportColumnType.java index 24b566e631..a0cd40251f 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/BulkImportColumnType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/importing/csv/BulkImportColumnType.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.ie.importing.csv; +package org.thingsboard.server.common.data.sync.ie.importing.csv; import lombok.Getter; import org.thingsboard.server.common.data.DataConstants; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/BulkImportRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/importing/csv/BulkImportRequest.java similarity index 94% rename from application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/BulkImportRequest.java rename to common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/importing/csv/BulkImportRequest.java index e8eac6a9ed..8d7b6f9d82 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/BulkImportRequest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/importing/csv/BulkImportRequest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.ie.importing.csv; +package org.thingsboard.server.common.data.sync.ie.importing.csv; import lombok.Data; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/BulkImportResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/importing/csv/BulkImportResult.java similarity index 94% rename from application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/BulkImportResult.java rename to common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/importing/csv/BulkImportResult.java index 0626c8e690..cced74afaa 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/BulkImportResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/importing/csv/BulkImportResult.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.ie.importing.csv; +package org.thingsboard.server.common.data.sync.ie.importing.csv; import lombok.Data; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/BranchInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/BranchInfo.java index fcf1f4470e..c4dc3d8e07 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/BranchInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/BranchInfo.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.sync.vc; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; import java.util.Objects; @@ -22,6 +23,8 @@ import java.util.Objects; @Data public class BranchInfo { private final String name; + + @JsonProperty("default") private final boolean isDefault; @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/load/VersionLoadRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/load/VersionLoadRequest.java index d9d1329c8b..cb60d91b24 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/load/VersionLoadRequest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/load/VersionLoadRequest.java @@ -29,7 +29,6 @@ import static com.fasterxml.jackson.annotation.JsonSubTypes.Type; @Data public abstract class VersionLoadRequest { - private String branch; private String versionId; public abstract VersionLoadRequestType getType(); diff --git a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java index 392c686a14..7d1710a87b 100644 --- a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java +++ b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java @@ -354,7 +354,11 @@ public class GitRepository { } private ObjectId resolve(String rev) throws IOException { - return git.getRepository().resolve(rev); + ObjectId result = git.getRepository().resolve(rev); + if (result == null) { + throw new IllegalArgumentException("Failed to parse git revision string: \"" + rev + "\""); + } + return result; } private , T> T execute(C command) throws GitAPIException { diff --git a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java index fe78879125..e3c43be6f7 100644 --- a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java +++ b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java @@ -51,6 +51,7 @@ import org.thingsboard.server.common.data.DeviceProfileInfo; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.EntitySubtype; +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.Event; @@ -77,6 +78,7 @@ import org.thingsboard.server.common.data.audit.AuditLog; import org.thingsboard.server.common.data.device.DeviceSearchQuery; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeInfo; import org.thingsboard.server.common.data.edge.EdgeSearchQuery; import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; import org.thingsboard.server.common.data.id.AlarmId; @@ -134,6 +136,19 @@ import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.common.data.security.model.SecuritySettings; import org.thingsboard.server.common.data.security.model.UserPasswordPolicy; import org.thingsboard.server.common.data.sms.config.TestSmsRequest; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportRequest; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportResult; +import org.thingsboard.server.common.data.sync.vc.AutoCommitSettings; +import org.thingsboard.server.common.data.sync.vc.BranchInfo; +import org.thingsboard.server.common.data.sync.vc.EntityDataDiff; +import org.thingsboard.server.common.data.sync.vc.EntityDataInfo; +import org.thingsboard.server.common.data.sync.vc.EntityVersion; +import org.thingsboard.server.common.data.sync.vc.RepositorySettings; +import org.thingsboard.server.common.data.sync.vc.VersionCreationResult; +import org.thingsboard.server.common.data.sync.vc.VersionLoadResult; +import org.thingsboard.server.common.data.sync.vc.VersionedEntityInfo; +import org.thingsboard.server.common.data.sync.vc.request.create.VersionCreateRequest; +import org.thingsboard.server.common.data.sync.vc.request.load.VersionLoadRequest; import org.thingsboard.server.common.data.widget.WidgetType; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.common.data.widget.WidgetTypeInfo; @@ -147,11 +162,12 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.stream.Collectors; -import static org.springframework.util.StringUtils.isEmpty; +import static org.thingsboard.server.common.data.StringUtils.isEmpty; /** * @author Andrew Shvayka @@ -267,6 +283,60 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { return restTemplate.postForEntity(baseURL + "/api/admin/securitySettings", securitySettings, SecuritySettings.class).getBody(); } + public Optional getRepositorySettings() { + try { + ResponseEntity repositorySettings = restTemplate.getForEntity(baseURL + "/api/admin/repositorySettings", RepositorySettings.class); + return Optional.ofNullable(repositorySettings.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public Boolean repositorySettingsExists() { + return restTemplate.getForEntity(baseURL + "/api/admin/repositorySettings/exists", Boolean.class).getBody(); + } + + public RepositorySettings saveRepositorySettings(RepositorySettings repositorySettings) { + return restTemplate.postForEntity(baseURL + "/api/admin/repositorySettings", repositorySettings, RepositorySettings.class).getBody(); + } + + public void deleteRepositorySettings() { + restTemplate.delete(baseURL + "/api/admin/repositorySettings"); + } + + public void checkRepositoryAccess(RepositorySettings repositorySettings) { + restTemplate.postForLocation(baseURL + "/api/admin/repositorySettings/checkAccess", repositorySettings); + } + + public Optional getAutoCommitSettings() { + try { + ResponseEntity autoCommitSettings = restTemplate.getForEntity(baseURL + "/api/admin/autoCommitSettings", AutoCommitSettings.class); + return Optional.ofNullable(autoCommitSettings.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public Boolean autoCommitSettingsExists() { + return restTemplate.getForEntity(baseURL + "/api/admin/autoCommitSettings/exists", Boolean.class).getBody(); + } + + public AutoCommitSettings saveAutoCommitSettings(AutoCommitSettings autoCommitSettings) { + return restTemplate.postForEntity(baseURL + "/api/admin/autoCommitSettings", autoCommitSettings, AutoCommitSettings.class).getBody(); + } + + public void deleteAutoCommitSettings() { + restTemplate.delete(baseURL + "/api/admin/autoCommitSettings"); + } + public Optional checkUpdates() { try { ResponseEntity updateMsg = restTemplate.getForEntity(baseURL + "/api/admin/updates", UpdateMessage.class); @@ -550,6 +620,15 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { }).getBody(); } + public BulkImportResult processAssetsBulkImport(BulkImportRequest request) { + return restTemplate.exchange( + baseURL + "/api/asset/bulk_import", + HttpMethod.POST, + new HttpEntity<>(request), + new ParameterizedTypeReference>() { + }).getBody(); + } + @Deprecated public Optional findAsset(String name) { Map params = new HashMap(); @@ -1251,7 +1330,7 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { HttpEntity.EMPTY, Device.class, tenantId, deviceId).getBody(); } - public Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(OtaPackageType otaPackageType, DeviceProfileId deviceProfileId) { + public Long countByDeviceProfileAndEmptyOtaPackage(OtaPackageType otaPackageType, DeviceProfileId deviceProfileId) { Map params = new HashMap<>(); params.put("otaPackageType", otaPackageType.name()); params.put("deviceProfileId", deviceProfileId.getId().toString()); @@ -1266,6 +1345,15 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { ).getBody(); } + public BulkImportResult processDevicesBulkImport(BulkImportRequest request) { + return restTemplate.exchange( + baseURL + "/api/device/bulk_import", + HttpMethod.POST, + new HttpEntity<>(request), + new ParameterizedTypeReference>() { + }).getBody(); + } + @Deprecated public Device createDevice(String name, String type) { Device device = new Device(); @@ -2499,6 +2587,10 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { } } + public Boolean isEdgesSupportEnabled() { + return restTemplate.getForEntity(baseURL + "/api/edges/enabled", Boolean.class).getBody(); + } + public Edge saveEdge(Edge edge) { return restTemplate.postForEntity(baseURL + "/api/edge", edge, Edge.class).getBody(); } @@ -2520,6 +2612,19 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { } } + public Optional getEdgeInfoById(EdgeId edgeId) { + try { + ResponseEntity edge = restTemplate.getForEntity(baseURL + "/api/edge/info/{edgeId}", EdgeInfo.class, edgeId.getId()); + return Optional.ofNullable(edge.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + public Optional assignEdgeToCustomer(CustomerId customerId, EdgeId edgeId) { try { ResponseEntity edge = restTemplate.postForEntity(baseURL + "/api/customer/{customerId}/edge/{edgeId}", null, Edge.class, customerId.getId(), edgeId.getId()); @@ -2546,7 +2651,7 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { } } - public Optional setRootRuleChain(EdgeId edgeId, RuleChainId ruleChainId) { + public Optional setEdgeRootRuleChain(EdgeId edgeId, RuleChainId ruleChainId) { try { ResponseEntity ruleChain = restTemplate.postForEntity(baseURL + "/api/edge/{edgeId}/{ruleChainId}/root", null, Edge.class, edgeId.getId(), ruleChainId.getId()); return Optional.ofNullable(ruleChain.getBody()); @@ -2827,6 +2932,17 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { }, params).getBody(); } + public PageData getTenantEdgeInfos(String type, PageLink pageLink) { + Map params = new HashMap<>(); + params.put("type", type); + addPageLinkToParam(params, pageLink); + return restTemplate.exchange( + baseURL + "/api/tenant/edgeInfos?type={type}&" + getUrlParams(pageLink), + HttpMethod.GET, HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, params).getBody(); + } + public Optional getTenantEdge(String edgeName) { try { ResponseEntity edge = restTemplate.getForEntity(baseURL + "/api/tenant/edges?edgeName={edgeName}", Edge.class, edgeName); @@ -2852,6 +2968,18 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { }, params).getBody(); } + public PageData getCustomerEdgeInfos(CustomerId customerId, PageLink pageLink, String edgeType) { + Map params = new HashMap<>(); + params.put("customerId", customerId.getId().toString()); + params.put("type", edgeType); + addPageLinkToParam(params, pageLink); + return restTemplate.exchange( + baseURL + "/api/customer/{customerId}/edgeInfos?type={type}&" + getUrlParams(pageLink), + HttpMethod.GET, HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, params).getBody(); + } + public List getEdgesByIds(List edgeIds) { return restTemplate.exchange(baseURL + "/api/edges?edgeIds={edgeIds}", HttpMethod.GET, @@ -2896,6 +3024,138 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { restTemplate.postForEntity(baseURL + "/api/edge/sync/{edgeId}", null, EdgeId.class, params); } + public String findMissingToRelatedRuleChains(EdgeId edgeId) { + return restTemplate.getForEntity(baseURL + "/api/edge/missingToRelatedRuleChains/{edgeId}", String.class, edgeId.getId()).getBody(); + } + + public BulkImportResult processEdgesBulkImport(BulkImportRequest request) { + return restTemplate.exchange( + baseURL + "/api/edge/bulk_import", + HttpMethod.POST, + new HttpEntity<>(request), + new ParameterizedTypeReference>() { + }).getBody(); + } + + public UUID saveEntitiesVersion(VersionCreateRequest request) { + return restTemplate.postForEntity(baseURL + "/api/entities/vc/version", request, UUID.class).getBody(); + } + + public Optional getVersionCreateRequestStatus(UUID requestId) { + try { + ResponseEntity versionCreateResult = restTemplate.getForEntity(baseURL + "/api/entities/vc/version/{requestId}/status", VersionCreationResult.class, requestId); + return Optional.ofNullable(versionCreateResult.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + public PageData listEntityVersions(EntityId externalEntityId, String branch, PageLink pageLink) { + Map params = new HashMap<>(); + params.put("entityType", externalEntityId.getEntityType().name()); + params.put("externalEntityUuid", externalEntityId.getId().toString()); + params.put("branch", branch); + addPageLinkToParam(params, pageLink); + return restTemplate.exchange( + baseURL + "/api/entities/vc/version/{entityType}/{externalEntityUuid}?branch={branch}&" + getUrlParams(pageLink), + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, + params).getBody(); + } + + public PageData listEntityTypeVersions(EntityType entityType, String branch, PageLink pageLink) { + Map params = new HashMap<>(); + params.put("entityType", entityType.name()); + params.put("branch", branch); + addPageLinkToParam(params, pageLink); + return restTemplate.exchange( + baseURL + "/api/entities/vc/version/{entityType}?branch={branch}&" + getUrlParams(pageLink), + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, + params).getBody(); + } + + public PageData listVersions(String branch, PageLink pageLink) { + Map params = new HashMap<>(); + params.put("branch", branch); + addPageLinkToParam(params, pageLink); + return restTemplate.exchange( + baseURL + "/api/entities/vc/version?branch={branch}&" + getUrlParams(pageLink), + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, + params).getBody(); + } + + public List listEntitiesAtVersion(EntityType entityType, String versionId) { + Map params = new HashMap<>(); + params.put("entityType", entityType.name()); + params.put("versionId", versionId); + return restTemplate.exchange( + baseURL + "/api/entities/vc/entity/{entityType}/{versionId}", + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, + params).getBody(); + } + + public List listAllEntitiesAtVersion(String versionId) { + Map params = new HashMap<>(); + params.put("versionId", versionId); + return restTemplate.exchange( + baseURL + "/api/entities/vc/entity/{versionId}", + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, + params).getBody(); + } + + public EntityDataInfo getEntityDataInfo(EntityId externalEntityId, String versionId) { + return restTemplate.getForEntity(baseURL + "/api/entities/vc/info/{versionId}/{entityType}/{externalEntityUuid}", + EntityDataInfo.class, versionId, externalEntityId.getEntityType(), externalEntityId.getId()).getBody(); + } + + public EntityDataDiff compareEntityDataToVersion(EntityId internalEntityId, String versionId) { + return restTemplate.getForEntity(baseURL + "/api/entities/vc/diff/{entityType}/{internalEntityUuid}?versionId={versionId}", + EntityDataDiff.class, internalEntityId.getEntityType(), internalEntityId.getId(), versionId).getBody(); + } + + public UUID loadEntitiesVersion(VersionLoadRequest request) { + return restTemplate.postForEntity(baseURL + "/api/entities/vc/entity", request, UUID.class).getBody(); + } + + public Optional getVersionLoadRequestStatus(UUID requestId) { + try { + ResponseEntity versionLoadResult = restTemplate.getForEntity(baseURL + "/api/entities/vc/entity/{requestId}/status", VersionLoadResult.class, requestId); + return Optional.ofNullable(versionLoadResult.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public List listBranches() { + return restTemplate.exchange( + baseURL + "/api/entities/vc/branches", + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }).getBody(); + } + public ResponseEntity downloadResource(TbResourceId resourceId) { Map params = new HashMap<>(); params.put("resourceId", resourceId.getId().toString()); diff --git a/ui-ngx/src/app/core/http/entities-version-control.service.ts b/ui-ngx/src/app/core/http/entities-version-control.service.ts index 447f252fad..b71c95b3ec 100644 --- a/ui-ngx/src/app/core/http/entities-version-control.service.ts +++ b/ui-ngx/src/app/core/http/entities-version-control.service.ts @@ -158,12 +158,10 @@ export class EntitiesVersionControlService { defaultHttpOptionsFromConfig({...config, ...{ignoreLoading: true}})); } - public compareEntityDataToVersion(branch: string, - entityId: EntityId, + public compareEntityDataToVersion(entityId: EntityId, versionId: string, config?: RequestConfig): Observable { - const encodedBranch = encodeURIComponent(branch); - return this.http.get(`/api/entities/vc/diff/${entityId.entityType}/${entityId.id}?branch=${encodedBranch}&versionId=${versionId}`, + return this.http.get(`/api/entities/vc/diff/${entityId.entityType}/${entityId.id}?versionId=${versionId}`, defaultHttpOptionsFromConfig(config)); } diff --git a/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.ts b/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.ts index 718dc5c2e7..dda0f6b39d 100644 --- a/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.ts +++ b/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.ts @@ -41,9 +41,6 @@ import { parseHttpErrorMessage } from '@core/utils'; }) export class ComplexVersionLoadComponent extends PageComponent implements OnInit, OnDestroy { - @Input() - branch: string; - @Input() versionName: string; @@ -117,7 +114,6 @@ export class ComplexVersionLoadComponent extends PageComponent implements OnInit restore(): void { const request: EntityTypeVersionLoadRequest = { - branch: this.branch, versionId: this.versionId, entityTypes: this.loadVersionFormGroup.get('entityTypes').value, type: VersionLoadRequestType.ENTITY_TYPE diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-version-diff.component.ts b/ui-ngx/src/app/modules/home/components/vc/entity-version-diff.component.ts index eb9fe8118b..8cc4a3b008 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-version-diff.component.ts +++ b/ui-ngx/src/app/modules/home/components/vc/entity-version-diff.component.ts @@ -59,9 +59,6 @@ export class EntityVersionDiffComponent extends PageComponent implements OnInit, @ViewChild('diffViewer', {static: true}) diffViewerElmRef: ElementRef; - @Input() - branch: string; - @Input() versionName: string; @@ -108,7 +105,7 @@ export class EntityVersionDiffComponent extends PageComponent implements OnInit, ngOnInit(): void { this.entitiesVersionControlService - .compareEntityDataToVersion(this.branch, this.entityId, this.versionId).subscribe((diffData) => { + .compareEntityDataToVersion(this.entityId, this.versionId).subscribe((diffData) => { const leftContent = entityExportDataToJsonString(diffData.currentVersion); const rightContent = entityExportDataToJsonString(diffData.otherVersion); const leftLines = leftContent.split('\n').length; @@ -310,7 +307,6 @@ export class EntityVersionDiffComponent extends PageComponent implements OnInit, const restoreVersionPopover = this.popoverService.displayPopover(trigger, this.renderer, this.viewContainerRef, EntityVersionRestoreComponent, 'leftTop', true, null, { - branch: this.branch, versionName: this.versionName, versionId: this.versionId, externalEntityId: this.externalEntityId, diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.ts b/ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.ts index 43d2452ea3..44f86129d8 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.ts +++ b/ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.ts @@ -41,9 +41,6 @@ import { parseHttpErrorMessage } from '@core/utils'; }) export class EntityVersionRestoreComponent extends PageComponent implements OnInit, OnDestroy { - @Input() - branch: string; - @Input() versionName: string; @@ -108,7 +105,6 @@ export class EntityVersionRestoreComponent extends PageComponent implements OnIn restore(): void { const request: SingleEntityVersionLoadRequest = { - branch: this.branch, versionId: this.versionId, externalEntityId: this.externalEntityId, config: { diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.ts b/ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.ts index 9612a1c1b3..0c4a7ca477 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.ts @@ -260,7 +260,6 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni const diffVersionPopover = this.popoverService.displayPopover(trigger, this.renderer, this.viewContainerRef, EntityVersionDiffComponent, 'leftTop', true, null, { - branch: this.branch, versionName: entityVersion.name, versionId: entityVersion.id, entityId: this.entityId, @@ -284,7 +283,6 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni const restoreVersionPopover = this.popoverService.displayPopover(trigger, this.renderer, this.viewContainerRef, EntityVersionRestoreComponent, 'leftTop', true, null, { - branch: this.branch, versionName: entityVersion.name, versionId: entityVersion.id, externalEntityId: this.externalEntityIdValue, @@ -310,7 +308,6 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni const restoreEntitiesVersionPopover = this.popoverService.displayPopover(trigger, this.renderer, this.viewContainerRef, ComplexVersionLoadComponent, 'leftTop', true, null, { - branch: this.branch, versionName: entityVersion.name, versionId: entityVersion.id, onClose: (result: VersionLoadResult | null) => { diff --git a/ui-ngx/src/app/shared/models/vc.models.ts b/ui-ngx/src/app/shared/models/vc.models.ts index c8184a195a..432332fcee 100644 --- a/ui-ngx/src/app/shared/models/vc.models.ts +++ b/ui-ngx/src/app/shared/models/vc.models.ts @@ -113,7 +113,6 @@ export enum VersionLoadRequestType { } export interface VersionLoadRequest { - branch: string; versionId: string; type: VersionLoadRequestType; } From 361e96f611a9fd0e78327a0a49c8df5cf2251687 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 29 Jun 2022 18:04:53 +0300 Subject: [PATCH 20/20] refactoring: tests Asset add to delete .andExpect... --- .../server/controller/BaseAssetControllerTest.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java index 452d4f40f0..1e8bff0ee5 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java @@ -164,13 +164,15 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - doPost("/api/asset", savedAsset, Asset.class, status().isForbidden()); + doPost("/api/asset", savedAsset) + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); testNotifyEntityNever(savedAsset.getId(), savedAsset); - //TODO fix bug: "No value at JSON path "$.message" - doDelete("/api/asset" + savedAsset.getId().getId().toString()) - .andExpect(status().isNotFound()); + doDelete("/api/asset/" + savedAsset.getId().getId().toString()) + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); testNotifyEntityNever(savedAsset.getId(), savedAsset);